From 764ce8c7f24e4455e445fe58dae9bb9f88c2e04e Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 8 Dec 2025 12:06:35 -0500 Subject: [PATCH] xo-alloc2: utest working w/ object-model demo --- CMakeLists.txt | 32 ++++++ cmake/xo-bootstrap-macros.cmake | 35 ++++++ cmake/xo_alloc2Config.cmake.in | 35 ++++++ utest/objectmodel.test.cpp | 194 +++++++++++++++++++++++++------- 4 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 cmake/xo-bootstrap-macros.cmake create mode 100644 cmake/xo_alloc2Config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8e4c7a3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,32 @@ +# xo-alloc2/CMakeLists.txt + +cmake_minimum_required(VERSION 3.10) + +project(xo_alloc2 VERSION 0.1) + +include(GNUInstallDirs) +include(cmake/xo-bootstrap-macros.cmake) + +xo_cxx_toplevel_options3() + +# ---------------------------------------------------------------- +# c++ settings + +set(PROJECT_CXX_FLAGS "") +#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only! +add_definitions(${PROJECT_CXX_FLAGS}) + +# ---------------------------------------------------------------- + +# must complete definition of expression lib before configuring examples +#add_subdirectory(src/alloc) +add_subdirectory(utest) +#xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets) + +# ---------------------------------------------------------------- +# docs targets depend on other library/utest/exec targets above, +# --> must come after them. +# +#add_subdirectory(docs) + +# end CMakeLists.txt diff --git a/cmake/xo-bootstrap-macros.cmake b/cmake/xo-bootstrap-macros.cmake new file mode 100644 index 0000000..aba3116 --- /dev/null +++ b/cmake/xo-bootstrap-macros.cmake @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/cmake/xo_alloc2Config.cmake.in b/cmake/xo_alloc2Config.cmake.in new file mode 100644 index 0000000..aba3116 --- /dev/null +++ b/cmake/xo_alloc2Config.cmake.in @@ -0,0 +1,35 @@ +# ---------------------------------------------------------------- +# for example: +# $ PREFIX=/usr/local # for example +# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build +# +# will get +# CMAKE_MODULE_PATH +# from xo-cmake-config --cmake-module-path +# +# and expect .cmake macros in +# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake +# ---------------------------------------------------------------- + +find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED) + +if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND") + message(FATAL "could not find xo-cmake-config executable") +endif() + +message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}") + +if (NOT XO_SUBMODULE_BUILD) + if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix)) + # default to typical install location for xo-project-macros + execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH) + message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}") + endif() +endif() + +# needs to have been installed somewhere on CMAKE_MODULE_PATH, +# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX) +# +include(xo_macros/xo_cxx) + +xo_cxx_bootstrap_message() diff --git a/utest/objectmodel.test.cpp b/utest/objectmodel.test.cpp index 8ceb86b..19ce1b7 100644 --- a/utest/objectmodel.test.cpp +++ b/utest/objectmodel.test.cpp @@ -41,19 +41,28 @@ * ^ * | * RComplex - * = RoutingFor + * = RoutingFor::RoutingType * ^ * | * ubox * - * AComplex: abstract interface + * AComplex: abstract interface. + * explicit, type-erased, data pointer argument + * virtual AComplex::xcoord(void * data) + * * DPolarCoords: passive representation + * * IComplex_DPolarCoords: implement AComplex interface for representation DPolarCoords + * static methods with typed data pointer argument + * IComplex_DPolarCoords::xcoord(void * data) + * IComplex_DPolarCoords::_xcoord(DPolarCoords * data) * * OUniqueBox: * a self-sufficient object, associating * interface AComplex with representation DPolarCoords + * OUniqueBox .data() method is DPolarCoord* + * 'impure' in the sense that it mixes code+data * * RComplex: convenience interface for OUniqueBox * @@ -110,10 +119,10 @@ namespace xo { template struct IComplex_Specific : public AComplex { - double _xcoord(Repr *) const; - double _ycoord(Repr *) const; - double _argument(Repr *) const; - double _magnitude(Repr *) const; + static double _xcoord(Repr *); + static double _ycoord(Repr *); + static double _argument(Repr *); + static double _magnitude(Repr *); virtual double xcoord(void * data) const final override { return _xcoord((Repr*)data); } virtual double ycoord(void * data) const final override { return _ycoord((Repr*)data); } @@ -132,25 +141,28 @@ namespace xo { }; /** implementation of AComplex interface with representation DPolarCoords **/ - using struct IComplex_DPolarCoords = IComplex_Specific; + using IComplex_DPolarCoords = IComplex_Specific; template <> - IComplex_Specific::_xcoord(DPolarCoords * data) const { + double + IComplex_Specific::_xcoord(DPolarCoords * data) { return data->mag_ * std::cos(data->arg_); }; template <> - IComplex_Specific::_ycoord(DPolarCoords * data) const { + double IComplex_Specific::_ycoord(DPolarCoords * data) { return data->mag_ * std::sin(data->arg_); }; template <> - IComplex_Specific::_argument(DPolarCoords * data) const { + double + IComplex_Specific::_argument(DPolarCoords * data) { return data->arg_; } template <> - IComplex_Specific::_magnitude(DPolarCoords * data) const { + double + IComplex_Specific::_magnitude(DPolarCoords * data) { return data->mag_; } @@ -170,32 +182,34 @@ namespace xo { }; /** implementation of AComplex interface with representation DRectCoords **/ - using struct IComplex_DRectCoords = IComplex_Specific; + using IComplex_DRectCoords = IComplex_Specific; template <> - IComplex_Specific::_xcoord(DRectCoords * data) const { - return data->mag_ * std::cos(data->arg_); + double + IComplex_Specific::_xcoord(DRectCoords * data) { + return data->x_; }; template <> - IComplex_Specific::_ycoord(DRectCoords * data) const { - return data->mag_ * std::sin(data->arg_); + double + IComplex_Specific::_ycoord(DRectCoords * data) { + return data->y_; }; template <> - IComplex_Specific::_argument(DRectCoords * data) const { - return data->arg_; + double + IComplex_Specific::_argument(DRectCoords * data) { + return std::atan(data->y_ / data->x_); } template <> - IComplex_Specific::_magnitude(DRectCoords * data) const { - return data->mag_; - } + double + IComplex_Specific::_magnitude(DRectCoords * data) { + double x = data->x_; + double y = data->y_; - template <> - struct ISpecificFor { - using ImplType = IComplex_Specific; - }; + return std::sqrt(x*x + y*y); + } template <> struct ISpecificFor { @@ -204,8 +218,18 @@ namespace xo { // ----- box with unique pointer ----- - /** u for unique, b for box. Using lowercase for unobtrusiveness, - * so that in ub, MyType is naturally emphasized + /** + * Creates a 'classic object-oriented' + * instance that has both interface+data. + * + * OUniqueBox uses a unique_ptr to hold data, + * so lifetime ends (unless moved) when this OUniqueBox + * goes out of scope + * + * policy: + * In our object model, these are not intended to be used + * for state; instead create them just-in-time. + * * * @tparam ISpecific will be a specific interface, * such as ISpecificFor @@ -215,19 +239,26 @@ namespace xo { * z1._xcoord(z1.data()); **/ template - struct OUniqueBox : ISpecificFor::typename ImplType { + struct OUniqueBox : ISpecificFor::ImplType { + using DataType = Data; + using DataBox = std::unique_ptr; + + explicit OUniqueBox(DataBox d) : data_{std::move(d)} {} + Data * data() const { return data_.get(); } - up data_; + DataBox data_; }; template struct RComplex : public Object { - double xcoord() const { return _xcoord(data()); } - double ycoord() const { return _ycoord(data()); } - double argument() const { return _argument(data()); } - double magnitude() const { return _magnitude(data()); } - } + RComplex(Object::DataBox data) : Object{std::move(data)} {} + + double xcoord() const { return Object::_xcoord(Object::data()); } + double ycoord() const { return Object::_ycoord(Object::data()); } + double argument() const { return Object::_argument(Object::data()); } + double magnitude() const { return Object::_magnitude(Object::data()); } + }; template struct RoutingFor; @@ -237,6 +268,9 @@ namespace xo { using RoutingType = RComplex; }; + template + using RoutingType = RoutingFor::RoutingType; + /** boxed object, held by unique pointer * * Example: @@ -244,8 +278,92 @@ namespace xo { * z1.xcoord(); **/ template - struct ubox : public RoutingFor::typename RoutingType { - } + struct ubox : public RoutingType> { + using Super = RoutingType>; + + explicit ubox(Super::DataBox d) : Super{std::move(d)} {} + }; + } /*namespace*/ + + TEST_CASE("objectmodel-specific-1", "[objectmodel]") + { + /* arg=0, mag=1 -> 1+0i */ + DPolarCoords polar{0.0, 1.0}; + IComplex_Specific polar_iface; + + REQUIRE(polar_iface._xcoord(&polar) == 1.0); + REQUIRE(polar_iface._ycoord(&polar) == 0.0); + REQUIRE(polar_iface._argument(&polar) == 0.0); + REQUIRE(polar_iface._magnitude(&polar) == 1.0); } - } -} + + TEST_CASE("objectmodel-specific-2", "[objectmodel]") + { + /* arg=0, mag=1 -> 1+0i */ + DRectCoords rect{1.0, 0.0}; + IComplex_Specific rect_iface; + + REQUIRE(rect_iface._xcoord(&rect) == 1.0); + REQUIRE(rect_iface._ycoord(&rect) == 0.0); + REQUIRE(rect_iface._argument(&rect) == 0.0); + REQUIRE(rect_iface._magnitude(&rect) == 1.0); + } + + TEST_CASE("uniquebox-1", "[objectmodel]") + { + OUniqueBox box + {std::make_unique(0.0, 1.0)}; + + REQUIRE(box.xcoord(box.data()) == 1.0); + REQUIRE(box.ycoord(box.data()) == 0.0); + REQUIRE(box.argument(box.data()) == 0.0); + REQUIRE(box.magnitude(box.data()) == 1.0); + } + + TEST_CASE("router-1", "[objectmodel]") + { + using Object = OUniqueBox; + + RComplex box{std::make_unique(0.0, 1.0)}; + + REQUIRE(box.xcoord() == 1.0); + REQUIRE(box.ycoord() == 0.0); + REQUIRE(box.argument() == 0.0); + REQUIRE(box.magnitude() == 1.0); + } + + TEST_CASE("routing-type-1", "[objectmodel]") + { + using Object = OUniqueBox; + + RoutingType box{std::make_unique(0.0, 1.0)}; + + REQUIRE(box.xcoord() == 1.0); + REQUIRE(box.ycoord() == 0.0); + REQUIRE(box.argument() == 0.0); + REQUIRE(box.magnitude() == 1.0); + } + + TEST_CASE("ubox-1", "[objectmodel]") + { + ubox box{std::make_unique(0.0, 1.0)}; + + REQUIRE(box.xcoord() == 1.0); + REQUIRE(box.ycoord() == 0.0); + REQUIRE(box.argument() == 0.0); + REQUIRE(box.magnitude() == 1.0); + } + + TEST_CASE("ubox-2", "[objectmodel]") + { + ubox box{std::make_unique(1.0, 0.0)}; + + REQUIRE(box.xcoord() == 1.0); + REQUIRE(box.ycoord() == 0.0); + REQUIRE(box.argument() == 0.0); + REQUIRE(box.magnitude() == 1.0); + } + } /*namespace ut*/ +} /*namespace xo*/ + +/* end objectmodel.test.cp */