diff --git a/.github/workflows/devbuild.yml b/.github/workflows/devbuild.yml index 5ee32488..1eacbac0 100644 --- a/.github/workflows/devbuild.yml +++ b/.github/workflows/devbuild.yml @@ -159,7 +159,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=OFF \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" make buildext VERBOSE=1 - name: make pytest BUILD_QT=OFF @@ -167,7 +167,7 @@ jobs: python3 -c "import modmesh; assert modmesh.HAS_VIEW == False" make pytest VERBOSE=1 - - name: make buildext BUILD_QT=ON + - name: make buildext BUILD_QT=ON USE_PYTEST_HELPER_BINDING=OFF run: | rm -f build/*/Makefile make cmake \ @@ -177,7 +177,18 @@ jobs: CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" make buildext VERBOSE=1 - - name: make pytest BUILD_QT=ON + # build with pytest helper binding for testing + - name: make buildext BUILD_QT=ON USE_PYTEST_HELPER_BINDING=ON + run: | + rm -f build/*/Makefile + make cmake \ + VERBOSE=1 USE_CLANG_TIDY=OFF \ + BUILD_QT=ON \ + CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" + make buildext VERBOSE=1 + + - name: make pytest BUILD_QT=ON USE_PYTEST_HELPER_BINDING=ON run: | python3 -c "import modmesh; assert modmesh.HAS_VIEW == True" make pytest VERBOSE=1 @@ -189,7 +200,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=ON \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" - name: make run_viewer_pytest run: | @@ -205,7 +216,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=OFF \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_SANITIZER=OFF" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_SANITIZER=OFF -DUSE_PYTEST_HELPER_BINDING=ON" make buildext VERBOSE=1 make pytest VERBOSE=1 @@ -291,7 +302,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=OFF \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" make buildext VERBOSE=1 - name: make pytest BUILD_QT=OFF @@ -302,7 +313,7 @@ jobs: fi make pytest ${JOB_MAKE_ARGS} - - name: make buildext BUILD_QT=ON + - name: make buildext BUILD_QT=ON USE_PYTEST_HELPER_BINDING=OFF run: | rm -f build/*/Makefile make cmake \ @@ -312,7 +323,18 @@ jobs: CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" make buildext VERBOSE=1 - - name: make pytest BUILD_QT=ON + # build with pytest helper binding for testing + - name: make buildext BUILD_QT=ON USE_PYTEST_HELPER_BINDING=ON + run: | + rm -f build/*/Makefile + make cmake \ + VERBOSE=1 USE_CLANG_TIDY=OFF \ + BUILD_QT=ON \ + CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" + make buildext VERBOSE=1 + + - name: make pytest BUILD_QT=ON USE_PYTEST_HELPER_BINDING=ON run: | # PySide6 installed by pip will bundle with a prebuilt Qt, # this will cause duplicated symbol. @@ -331,7 +353,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=ON \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" - name: make run_viewer_pytest run: | @@ -346,7 +368,7 @@ jobs: VERBOSE=1 USE_CLANG_TIDY=OFF \ BUILD_QT=OFF \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_SANITIZER=OFF" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_SANITIZER=OFF -DUSE_PYTEST_HELPER_BINDING=ON" make buildext VERBOSE=1 make pytest VERBOSE=1 @@ -427,6 +449,7 @@ jobs: cmake ` -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} ` -Dpybind11_DIR="$(pybind11-config --cmakedir)" ` + -DUSE_PYTEST_HELPER_BINDING=ON ` -S${{ github.workspace }} ` -B${{ github.workspace }}/build cmake --build ${{ github.workspace }}/build ` diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 608fcd6d..084eac17 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -118,7 +118,7 @@ jobs: make viewer \ ${JOB_MAKE_ARGS} \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" - name: make run_viewer_pytest run: | @@ -126,7 +126,7 @@ jobs: make run_viewer_pytest \ ${JOB_MAKE_ARGS} \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" - name: make flake8 run: | @@ -208,7 +208,7 @@ jobs: make viewer \ ${JOB_MAKE_ARGS} \ CMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3)" + CMAKE_ARGS="-DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" - name: make run_viewer_pytest run: | diff --git a/.github/workflows/nouse_install.yml b/.github/workflows/nouse_install.yml index c31e606f..11ffddc8 100644 --- a/.github/workflows/nouse_install.yml +++ b/.github/workflows/nouse_install.yml @@ -71,7 +71,7 @@ jobs: - name: setup.py install build_ext run: | sudo python3 setup.py install build_ext \ - --cmake-args="${JOB_CMAKE_ARGS} -DPYTHON_EXECUTABLE=$(which python3)" \ + --cmake-args="${JOB_CMAKE_ARGS} -DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" \ --make-args="VERBOSE=1" - name: pytest @@ -154,7 +154,7 @@ jobs: # Using pip install instead of legacy install # ref: https://stackoverflow.com/questions/52375693/troubleshooting-pkg-resources-distributionnotfound-error python3 -m pip install -v . --no-use-pep517 --global-option=build_ext \ - --global-option="--cmake-args=${JOB_CMAKE_ARGS} -DPYTHON_EXECUTABLE=$(which python3)" \ + --global-option="--cmake-args=${JOB_CMAKE_ARGS} -DPYTHON_EXECUTABLE=$(which python3) -DUSE_PYTEST_HELPER_BINDING=ON" \ --global-option="--make-args="VERBOSE=1"" - name: pytest diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e273b6d..ab6cc99c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,13 @@ option(LINT_AS_ERRORS "clang-tidy warnings as errors" OFF) option(USE_SANITIZER "use sanitizer (undefined, leak, address)" OFF) +option(USE_PYTEST_HELPER_BINDING "use helper bindings to run pytest. should be ON if the build is for pytest" OFF) +if(USE_PYTEST_HELPER_BINDING) + add_definitions(-D USE_PYTEST_HELPER_BINDING) +endif() +message(STATUS "USE_PYTEST_HELPER_BINDING: ${USE_PYTEST_HELPER_BINDING}") + + if(BUILD_QT) option(QT3D_USE_RHI "Qt use RHI" OFF) message(STATUS "QT3D_USE_RHI: ${QT3D_USE_RHI}") @@ -82,8 +89,10 @@ message(STATUS "DEBUG_SYMBOL: ${DEBUG_SYMBOL}") if(DEBUG_SYMBOL) if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DEBUG") + add_compile_options(/O2 /Ob2) + string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") # /RTC1 incompatible with /O2 else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2") endif() endif() diff --git a/contrib/standalone_buffer/Makefile b/contrib/standalone_buffer/Makefile index 2a63e164..02bc77eb 100644 --- a/contrib/standalone_buffer/Makefile +++ b/contrib/standalone_buffer/Makefile @@ -38,16 +38,24 @@ $(SETUPROOT)/wrap_ConcreteBuffer.o: $(SETUPROOT)/modmesh/buffer/pymod/wrap_Concr $(SETUPROOT)/wrap_SimpleArray.o: $(SETUPROOT)/modmesh/buffer/pymod/wrap_SimpleArray.cpp Makefile c++ $(CFLAGS) -c $< -o $@ +$(SETUPROOT)/wrap_SimpleArrayPlex.o: $(SETUPROOT)/modmesh/buffer/pymod/wrap_SimpleArrayPlex.cpp Makefile + c++ $(CFLAGS) -c $< -o $@ + $(SETUPROOT)/buffer_pymod.o: $(SETUPROOT)/modmesh/buffer/pymod/buffer_pymod.cpp Makefile c++ $(CFLAGS) -c $< -o $@ +$(SETUPROOT)/SimpleArray.o: $(SETUPROOT)/modmesh/buffer/SimpleArray.cpp Makefile + c++ $(CFLAGS) -c $< -o $@ + $(SETUPROOT)/modbuf.o: modbuf.cpp c++ $(CFLAGS) -c $< -o $@ -$(MODPATH): Makefile $(SETUPROOT)/wrap_ConcreteBuffer.o $(SETUPROOT)/wrap_SimpleArray.o $(SETUPROOT)/buffer_pymod.o $(SETUPROOT)/modbuf.o +$(MODPATH): Makefile $(SETUPROOT)/SimpleArray.o $(SETUPROOT)/wrap_ConcreteBuffer.o $(SETUPROOT)/wrap_SimpleArray.o $(SETUPROOT)/wrap_SimpleArrayPlex.o $(SETUPROOT)/buffer_pymod.o $(SETUPROOT)/modbuf.o c++ $(LDFLAGS) \ + $(SETUPROOT)/SimpleArray.o \ $(SETUPROOT)/wrap_ConcreteBuffer.o \ $(SETUPROOT)/wrap_SimpleArray.o \ + $(SETUPROOT)/wrap_SimpleArrayPlex.o \ $(SETUPROOT)/buffer_pymod.o \ $(SETUPROOT)/modbuf.o \ -o $@ diff --git a/cpp/modmesh/CMakeLists.txt b/cpp/modmesh/CMakeLists.txt index b13fef3f..fa0642fd 100644 --- a/cpp/modmesh/CMakeLists.txt +++ b/cpp/modmesh/CMakeLists.txt @@ -40,6 +40,10 @@ add_subdirectory(spacetime) add_subdirectory(view) add_subdirectory(inout) +if(USE_PYTEST_HELPER_BINDING) + add_subdirectory(testhelper) +endif() # USE_PYTEST_HELPER_BINDING + set(MODMESH_ROOT_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/modmesh.hpp ${CMAKE_CURRENT_SOURCE_DIR}/base.hpp @@ -84,6 +88,7 @@ set(MODMESH_TERMINAL_FILES ${MODMESH_SPACETIME_FILES} ${MODMESH_PYTHON_FILES} ${MODMESH_INOUT_FILES} + ${MODMESH_TESTHELPER_FILES} CACHE FILEPATH "" FORCE) set(MODMESH_GRAPHIC_FILES diff --git a/cpp/modmesh/buffer/CMakeLists.txt b/cpp/modmesh/buffer/CMakeLists.txt index c83f93f1..ebe0e526 100644 --- a/cpp/modmesh/buffer/CMakeLists.txt +++ b/cpp/modmesh/buffer/CMakeLists.txt @@ -11,17 +11,20 @@ set(MODMESH_BUFFER_HEADERS CACHE FILEPATH "" FORCE) set(MODMESH_BUFFER_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/SimpleArray.cpp CACHE FILEPATH "" FORCE) set(MODMESH_BUFFER_PYMODHEADERS ${CMAKE_CURRENT_SOURCE_DIR}/pymod/buffer_pymod.hpp ${CMAKE_CURRENT_SOURCE_DIR}/pymod/TypeBroadcast.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/pymod/SimpleArrayCaster.hpp CACHE FILEPATH "" FORCE) set(MODMESH_BUFFER_PYMODSOURCES ${CMAKE_CURRENT_SOURCE_DIR}/pymod/buffer_pymod.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pymod/wrap_ConcreteBuffer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/pymod/wrap_SimpleArray.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pymod/wrap_SimpleArrayPlex.cpp CACHE FILEPATH "" FORCE) set(MODMESH_BUFFER_FILES diff --git a/cpp/modmesh/buffer/SimpleArray.cpp b/cpp/modmesh/buffer/SimpleArray.cpp new file mode 100644 index 00000000..735506b6 --- /dev/null +++ b/cpp/modmesh/buffer/SimpleArray.cpp @@ -0,0 +1,542 @@ +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +namespace modmesh +{ + +DataType get_data_type_from_string(const std::string & data_type_string) +{ + if (data_type_string == "bool") + { + return DataType::Bool; + } + if (data_type_string == "int8") + { + return DataType::Int8; + } + if (data_type_string == "int16") + { + return DataType::Int16; + } + if (data_type_string == "int32") + { + return DataType::Int32; + } + if (data_type_string == "int64") + { + return DataType::Uint64; + } + if (data_type_string == "uint8") + { + return DataType::Uint8; + } + if (data_type_string == "uint16") + { + return DataType::Uint16; + } + if (data_type_string == "uint32") + { + return DataType::Uint32; + } + if (data_type_string == "uint64") + { + return DataType::Uint64; + } + if (data_type_string == "float32") + { + return DataType::Float32; + } + if (data_type_string == "float64") + { + return DataType::Float64; + } + throw std::runtime_error("Unsupported datatype"); +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Bool; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Int8; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Int16; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Int32; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Int64; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Uint8; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Uint16; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Uint32; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Uint64; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Float32; +} + +template <> +DataType get_data_type_from_type() +{ + return DataType::Float64; +} + +SimpleArrayPlex::SimpleArrayPlex(const shape_type & shape, DataType data_type) + : m_data_type(data_type) + , m_has_instance_ownership(true) +{ + switch (data_type) + { + case DataType::Bool: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayBool(shape)); + break; + } + case DataType::Int8: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt8(shape)); + break; + } + case DataType::Int16: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt16(shape)); + break; + } + case DataType::Int32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt32(shape)); + break; + } + case DataType::Int64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt64(shape)); + break; + } + case DataType::Uint8: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint8(shape)); + break; + } + case DataType::Uint16: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint16(shape)); + break; + } + case DataType::Uint32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint32(shape)); + break; + } + case DataType::Uint64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint64(shape)); + break; + } + case DataType::Float32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat32(shape)); + break; + } + case DataType::Float64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat64(shape)); + break; + } + default: + { + throw std::runtime_error("Unsupported datatype"); + } + } +} + +SimpleArrayPlex::SimpleArrayPlex(SimpleArrayPlex const & other) + : m_data_type(other.m_data_type) +{ + if (!other.m_instance_ptr) + { + return; // other does not have instance + } + + m_has_instance_ownership = true; + + switch (other.m_data_type) + { + case DataType::Bool: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayBool(*array)); + break; + } + case DataType::Int8: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt8(*array)); + break; + } + case DataType::Int16: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt16(*array)); + break; + } + case DataType::Int32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt32(*array)); + break; + } + case DataType::Int64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt64(*array)); + break; + } + case DataType::Uint8: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint8(*array)); + break; + } + case DataType::Uint16: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint16(*array)); + break; + } + case DataType::Uint32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint32(*array)); + break; + } + case DataType::Uint64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint64(*array)); + break; + } + case DataType::Float32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat32(*array)); + break; + } + case DataType::Float64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat64(*array)); + break; + } + default: + { + throw std::runtime_error("Unsupported datatype"); + } + } +} + +SimpleArrayPlex::SimpleArrayPlex(SimpleArrayPlex && other) + : m_data_type(other.m_data_type) +{ + if (!other.m_instance_ptr) + { + return; // other does not have instance + } + + // take onwership + m_has_instance_ownership = true; + other.m_has_instance_ownership = false; + + m_instance_ptr = other.m_instance_ptr; +} + +SimpleArrayPlex & SimpleArrayPlex::operator=(SimpleArrayPlex const & other) +{ + if (this == &other) + { + return *this; + } + + m_data_type = other.m_data_type; + + if (!other.m_instance_ptr) + { + return *this; // other does not have instance + } + + m_has_instance_ownership = true; + + switch (other.m_data_type) + { + case DataType::Bool: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayBool(*array)); + break; + } + case DataType::Int8: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt8(*array)); + break; + } + case DataType::Int16: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt16(*array)); + break; + } + case DataType::Int32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt32(*array)); + break; + } + case DataType::Int64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayInt64(*array)); + break; + } + case DataType::Uint8: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint8(*array)); + break; + } + case DataType::Uint16: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint16(*array)); + break; + } + case DataType::Uint32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint32(*array)); + break; + } + case DataType::Uint64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayUint64(*array)); + break; + } + case DataType::Float32: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat32(*array)); + break; + } + case DataType::Float64: + { + const auto * array = static_cast(other.m_instance_ptr); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + m_instance_ptr = reinterpret_cast(new SimpleArrayFloat64(*array)); + break; + } + default: + { + throw std::runtime_error("Unsupported datatype"); + } + } + return *this; +} + +SimpleArrayPlex & SimpleArrayPlex::operator=(SimpleArrayPlex && other) +{ + m_data_type = other.m_data_type; + + if (!other.m_instance_ptr) + { + return *this; // other does not have instance + } + + // take onwership + m_has_instance_ownership = true; + other.m_has_instance_ownership = false; + + m_instance_ptr = other.m_instance_ptr; + return *this; +} + +SimpleArrayPlex::~SimpleArrayPlex() +{ + if (m_instance_ptr == nullptr || !m_has_instance_ownership) + { + return; + } + + switch (m_data_type) + { + case DataType::Bool: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Int8: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Int16: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Int32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Int64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Uint8: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Uint16: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Uint32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Uint64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Float32: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + case DataType::Float64: + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + delete reinterpret_cast(m_instance_ptr); + break; + } + default: + break; + } + + m_instance_ptr = nullptr; + m_has_instance_ownership = false; +} + +} /* end namespace modmesh */ diff --git a/cpp/modmesh/buffer/SimpleArray.hpp b/cpp/modmesh/buffer/SimpleArray.hpp index 1248852d..f5d6c448 100644 --- a/cpp/modmesh/buffer/SimpleArray.hpp +++ b/cpp/modmesh/buffer/SimpleArray.hpp @@ -30,8 +30,8 @@ #include -#include #include +#include #if defined(_MSC_VER) #include @@ -40,7 +40,6 @@ typedef SSIZE_T ssize_t; namespace modmesh { - namespace detail { @@ -82,12 +81,15 @@ inline size_t buffer_offset(small_vector const & stride, small_vector; +using sshape_type = small_vector; + template struct SimpleArrayInternalTypes { using value_type = T; - using shape_type = small_vector; - using sshape_type = small_vector; + using shape_type = detail::shape_type; + using sshape_type = detail::sshape_type; using buffer_type = ConcreteBuffer; }; /* end class SimpleArrayInternalType */ @@ -643,7 +645,6 @@ class SimpleArray size_t m_nghost = 0; value_type * m_body = nullptr; - }; /* end class SimpleArray */ template @@ -654,6 +655,86 @@ using is_simple_array = std::is_same< template inline constexpr bool is_simple_array_v = is_simple_array::value; +using SimpleArrayBool = SimpleArray; +using SimpleArrayInt8 = SimpleArray; +using SimpleArrayInt16 = SimpleArray; +using SimpleArrayInt32 = SimpleArray; +using SimpleArrayInt64 = SimpleArray; +using SimpleArrayUint8 = SimpleArray; +using SimpleArrayUint16 = SimpleArray; +using SimpleArrayUint32 = SimpleArray; +using SimpleArrayUint64 = SimpleArray; +using SimpleArrayFloat32 = SimpleArray; +using SimpleArrayFloat64 = SimpleArray; + +enum class DataType +{ + Undefined, + Bool, + Int8, + Int16, + Int32, + Int64, + Uint8, + Uint16, + Uint32, + Uint64, + Float32, + Float64, +}; /* end enum class DataType */ + +DataType get_data_type_from_string(const std::string & data_type_string); + +template +DataType get_data_type_from_type(); + +class SimpleArrayPlex +{ +public: + using shape_type = detail::shape_type; + + SimpleArrayPlex() = default; + + explicit SimpleArrayPlex(const shape_type & shape, const std::string & data_type) + : SimpleArrayPlex(shape, get_data_type_from_string(data_type)) + { + } + + explicit SimpleArrayPlex(const shape_type & shape, DataType data_type); + + template + SimpleArrayPlex(const SimpleArray & array) + { + m_data_type = get_data_type_from_type(); + m_has_instance_ownership = true; + m_instance_ptr = reinterpret_cast(new SimpleArray(array)); + } + + SimpleArrayPlex(SimpleArrayPlex const & other); + SimpleArrayPlex(SimpleArrayPlex && other); + SimpleArrayPlex & operator=(SimpleArrayPlex const & other); + SimpleArrayPlex & operator=(SimpleArrayPlex && other); + + ~SimpleArrayPlex(); + + DataType data_type() const + { + return m_data_type; + } + + const void * instance_ptr() const + { + return m_instance_ptr; + } + + /// TODO: add all SimpleArray public methods + +private: + bool m_has_instance_ownership = false; /// ownership of the instance + void * m_instance_ptr = nullptr; /// the pointer of the SimpleArray instance + DataType m_data_type = DataType::Undefined; /// the data type for array casting +}; /* end class SimpleArrayPlex */ + } /* end namespace modmesh */ /* vim: set et ts=4 sw=4: */ diff --git a/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp b/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp new file mode 100644 index 00000000..e9d08e4c --- /dev/null +++ b/cpp/modmesh/buffer/pymod/SimpleArrayCaster.hpp @@ -0,0 +1,104 @@ +#pragma once +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include + +#include + +/* + * The purpose of including this header is to facilitate implicit casting of + * SimpleArray types, such as casting from SimpleArrayPlex to SimplyArray. + * It should be incorporated whenever there is a C++ function wrapped by + * Pybind11 that involves SimpleArray as either parameters or a return value. + **/ + +namespace pybind11 +{ +namespace detail +{ + +// Define the Pybind11 caster for SimpleArray +#define SIMPLE_ARRAY_CASTER(DATATYPE) \ + template <> /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + struct type_caster : public type_caster_base \ + { \ + using base = type_caster_base; \ + \ + public: \ + /* Conversion from Python object to C++ */ \ + bool load(pybind11::handle src, bool convert) \ + { \ + /* Check if the source is SimpleArray */ \ + if (base::load(src, convert)) \ + { \ + return true; \ + } \ + /* Check if the source object is a valid SimpleArrayPlex */ \ + if (!pybind11::isinstance(src)) \ + { \ + return false; \ + } \ + \ + /* Get the SimpleArrayPlex object from the source handle */ \ + modmesh::SimpleArrayPlex arrayplex = src.cast(); \ + \ + /* Check if the data type is matched */ \ + if (arrayplex.data_type() != modmesh::DataType::DATATYPE) \ + { \ + return false; \ + } \ + \ + /* construct the new array from the arrayplex */ \ + const modmesh::SimpleArray##DATATYPE * array_from_arrayplex = reinterpret_cast(arrayplex.instance_ptr()); \ + value = const_cast(array_from_arrayplex); \ + return true; \ + } \ + \ + /* Conversion from C++ to Python object */ \ + static pybind11::handle cast(modmesh::SimpleArray##DATATYPE && src, pybind11::return_value_policy policy, pybind11::handle parent) \ + { \ + return base::cast(src, policy, parent); \ + } \ + } + +SIMPLE_ARRAY_CASTER(Bool); +SIMPLE_ARRAY_CASTER(Int8); +SIMPLE_ARRAY_CASTER(Int16); +SIMPLE_ARRAY_CASTER(Int32); +SIMPLE_ARRAY_CASTER(Int64); +SIMPLE_ARRAY_CASTER(Uint8); +SIMPLE_ARRAY_CASTER(Uint16); +SIMPLE_ARRAY_CASTER(Uint32); +SIMPLE_ARRAY_CASTER(Uint64); +SIMPLE_ARRAY_CASTER(Float32); +SIMPLE_ARRAY_CASTER(Float64); + +} /* end namespace detail */ +} /* end namespace pybind11 */ + +// vim: set ff=unix fenc=utf8 nobomb et sw=4 ts=4 sts=4: diff --git a/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp b/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp index b623759e..d1e2c202 100644 --- a/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp +++ b/cpp/modmesh/buffer/pymod/TypeBroadcast.hpp @@ -1,7 +1,7 @@ #pragma once /* - * Copyright (c) 2019, Yung-Yu Chen + * Copyright (c) 2024, An-Chi Liu * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/cpp/modmesh/buffer/pymod/buffer_pymod.cpp b/cpp/modmesh/buffer/pymod/buffer_pymod.cpp index 1a7a07ac..72666256 100644 --- a/cpp/modmesh/buffer/pymod/buffer_pymod.cpp +++ b/cpp/modmesh/buffer/pymod/buffer_pymod.cpp @@ -51,6 +51,7 @@ void initialize_buffer(pybind11::module & mod) wrap_ConcreteBuffer(mod); wrap_SimpleArray(mod); + wrap_SimpleArrayPlex(mod); }; OneTimeInitializer::me()(mod, initialize_impl); diff --git a/cpp/modmesh/buffer/pymod/buffer_pymod.hpp b/cpp/modmesh/buffer/pymod/buffer_pymod.hpp index ec67fe6f..9f1c4003 100644 --- a/cpp/modmesh/buffer/pymod/buffer_pymod.hpp +++ b/cpp/modmesh/buffer/pymod/buffer_pymod.hpp @@ -42,6 +42,7 @@ namespace python void initialize_buffer(pybind11::module & mod); void wrap_ConcreteBuffer(pybind11::module & mod); void wrap_SimpleArray(pybind11::module & mod); +void wrap_SimpleArrayPlex(pybind11::module & mod); } /* end namespace python */ diff --git a/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp b/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp index 3506006c..c2d07647 100644 --- a/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp +++ b/cpp/modmesh/buffer/pymod/wrap_SimpleArray.cpp @@ -27,8 +27,9 @@ */ #include // Must be the first include. -#include + #include +#include namespace modmesh { @@ -156,6 +157,7 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapSimpleArray .def_property_readonly("has_ghost", &wrapped_type::has_ghost) .def_property("nghost", &wrapped_type::nghost, &wrapped_type::set_nghost) .def_property_readonly("nbody", &wrapped_type::nbody) + .def_property_readonly("plex", &get_arrayplex) .wrap_modifiers() .wrap_calculators() // @@ -407,6 +409,11 @@ class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapSimpleArray return shape; } + static pybind11::object get_arrayplex(wrapped_type const & arr) + { + return pybind11::cast(SimpleArrayPlex(arr)); + } + }; /* end class WrapSimpleArray */ void wrap_SimpleArray(pybind11::module & mod) diff --git a/cpp/modmesh/buffer/pymod/wrap_SimpleArrayPlex.cpp b/cpp/modmesh/buffer/pymod/wrap_SimpleArrayPlex.cpp new file mode 100644 index 00000000..095ec85d --- /dev/null +++ b/cpp/modmesh/buffer/pymod/wrap_SimpleArrayPlex.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include // Must be the first include. + +namespace modmesh +{ + +namespace python +{ + +class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapSimpleArrayPlex : public WrapBase +{ + using root_base_type = WrapBase; + using wrapped_type = typename root_base_type::wrapped_type; + using wrapper_type = typename root_base_type::wrapper_type; + using shape_type = modmesh::detail::shape_type; + + friend root_base_type; + + WrapSimpleArrayPlex(pybind11::module & mod, char const * pyname, char const * pydoc) + : root_base_type(mod, pyname, pydoc, pybind11::buffer_protocol()) + { + (*this) + .def_timed( + pybind11::init( + [](pybind11::object const & shape, std::string const & datatype) + { return wrapped_type(make_shape(shape), datatype); }), + pybind11::arg("shape"), + pybind11::arg("dtype")) + .def_property_readonly("typed", &get_typed_array) + /// TODO: should have the same interface as WrapSimpleArray + ; + } + + /// return the typed function from the arrayplex + static pybind11::object get_typed_array(wrapped_type const & array_plex) + { + switch (array_plex.data_type()) + { + case DataType::Bool: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayBool(*array))); + } + case DataType::Int8: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayInt8(*array))); + } + case DataType::Int16: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayInt16(*array))); + } + case DataType::Int32: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayInt32(*array))); + } + case DataType::Int64: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayInt64(*array))); + } + case DataType::Uint8: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayUint8(*array))); + } + case DataType::Uint16: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayUint16(*array))); + } + case DataType::Uint32: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayUint32(*array))); + } + case DataType::Uint64: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayUint64(*array))); + } + case DataType::Float32: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayFloat32(*array))); + } + case DataType::Float64: + { + const auto * array = static_cast(array_plex.instance_ptr()); + return pybind11::cast(std::move(SimpleArrayFloat64(*array))); + } + default: + { + throw std::runtime_error("Unsupported datatype"); + } + } + } + + static shape_type make_shape(pybind11::object const & shape_in) + { + shape_type shape; + try + { + shape.push_back(shape_in.cast()); + } + catch (const pybind11::cast_error &) + { + shape = shape_in.cast>(); + } + return shape; + } +}; /* end of class WrapSimpleArrayPlex*/ + +void wrap_SimpleArrayPlex(pybind11::module & mod) +{ + WrapSimpleArrayPlex::commit(mod, "SimpleArray", "SimpleArray"); +} + +} /* end namespace python */ + +} /* end namespace modmesh */ + +// vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: diff --git a/cpp/modmesh/python/module.cpp b/cpp/modmesh/python/module.cpp index 5e97fbc2..29236df8 100644 --- a/cpp/modmesh/python/module.cpp +++ b/cpp/modmesh/python/module.cpp @@ -20,14 +20,18 @@ */ #include // Must be the first include. -#include -#include + #include -#include +#include #include #include +#include #include -#include +#include +#include +#ifdef USE_PYTEST_HELPER_BINDING +#include +#endif #ifdef QT_CORE_LIB #include #endif // QT_CORE_LIB @@ -49,6 +53,12 @@ void initialize(pybind11::module_ mod) initialize_spacetime(spacetime_mod); pybind11::module_ onedim_mod = mod.def_submodule("onedim", "onedim"); initialize_onedim(onedim_mod); + +#ifdef USE_PYTEST_HELPER_BINDING + pybind11::module_ testhelper_mod = mod.def_submodule("testhelper", "testhelper"); + initialize_testbuffer(testhelper_mod); +#endif + #ifdef QT_CORE_LIB mod.attr("HAS_VIEW") = true; pybind11::module_ view_mod = mod.def_submodule("view", "view"); diff --git a/cpp/modmesh/testhelper/CMakeLists.txt b/cpp/modmesh/testhelper/CMakeLists.txt new file mode 100644 index 00000000..a2db6a98 --- /dev/null +++ b/cpp/modmesh/testhelper/CMakeLists.txt @@ -0,0 +1,28 @@ +# Copyright (c) 2024, An-Chi Liu +# BSD-style license; see COPYING + +cmake_minimum_required(VERSION 3.16) + +set(MODMESH_TESTHELPER_HEADERS + CACHE FILEPATH "" FORCE) + +set(MODMESH_TESTHELPER_SOURCES + CACHE FILEPATH "" FORCE) + +set(MODMESH_TESTHELPER_PYMODHEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/pymod/testbuffer_pymod.hpp + CACHE FILEPATH "" FORCE) + +set(MODMESH_TESTHELPER_PYMODSOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/pymod/testbuffer_pymod.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pymod/wrap_TestSimpleArrayHelper.cpp + CACHE FILEPATH "" FORCE) + +set(MODMESH_TESTHELPER_FILES + ${MODMESH_TESTHELPER_HEADERS} + ${MODMESH_TESTHELPER_SOURCES} + ${MODMESH_TESTHELPER_PYMODHEADERS} + ${MODMESH_TESTHELPER_PYMODSOURCES} + CACHE FILEPATH "" FORCE) + +# vim: set ff=unix fenc=utf8 nobomb et sw=4 ts=4 sts=4: diff --git a/cpp/modmesh/testhelper/pymod/testbuffer_pymod.cpp b/cpp/modmesh/testhelper/pymod/testbuffer_pymod.cpp new file mode 100644 index 00000000..1887a6db --- /dev/null +++ b/cpp/modmesh/testhelper/pymod/testbuffer_pymod.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include // Must be the first include. + +#include +#include +#include + +namespace modmesh +{ +namespace python +{ + +struct testbuffer_pymod_tag +{ +}; + +template <> +OneTimeInitializer & OneTimeInitializer::me() +{ + static OneTimeInitializer instance; + return instance; +} + +void initialize_testbuffer(pybind11::module & mod) +{ + auto initialize_impl = [](pybind11::module & mod) + { + wrap_TestSimpleArrayHelper(mod); + }; + + OneTimeInitializer::me()(mod, initialize_impl); +} + +} /* end namespace python */ + +} /* end namespace modmesh */ diff --git a/cpp/modmesh/testhelper/pymod/testbuffer_pymod.hpp b/cpp/modmesh/testhelper/pymod/testbuffer_pymod.hpp new file mode 100644 index 00000000..940472ee --- /dev/null +++ b/cpp/modmesh/testhelper/pymod/testbuffer_pymod.hpp @@ -0,0 +1,49 @@ +#pragma once +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include // Must be the first include. +#include + +#include + +namespace modmesh +{ + +namespace python +{ + +void initialize_testbuffer(pybind11::module & mod); + +void wrap_TestSimpleArrayHelper(pybind11::module & mod); + +} /* end namespace python */ + +} /* end namespace modmesh */ + +// vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: \ No newline at end of file diff --git a/cpp/modmesh/testhelper/pymod/wrap_TestSimpleArrayHelper.cpp b/cpp/modmesh/testhelper/pymod/wrap_TestSimpleArrayHelper.cpp new file mode 100644 index 00000000..744adb6c --- /dev/null +++ b/cpp/modmesh/testhelper/pymod/wrap_TestSimpleArrayHelper.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, An-Chi Liu + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include // Must be the first include. + +#include +#include + +namespace modmesh +{ + +namespace python +{ + +struct TestSimpleArrayHelper +{ +}; /* end of TestSimpleArrayHelper */ + +class MODMESH_PYTHON_WRAPPER_VISIBILITY WrapTestSimpleArrayHelper + : public WrapBase +{ + +public: + + friend root_base_type; + +protected: + + WrapTestSimpleArrayHelper(pybind11::module & mod, char const * pyname, char const * pydoc) + : root_base_type(mod, pyname, pydoc) + { + (*this) + .def("test_cast_to_arrayint32", []() -> modmesh::SimpleArrayInt32 + { + // clang-format off + SimpleArrayInt32 arr(100); + return arr; + // clang-format on + }) + .def("test_load_arrayin32_from_arrayplex", [](modmesh::SimpleArrayInt32 &) -> bool + { return true; }) + .def("test_load_arrayfloat64_from_arrayplex", [](modmesh::SimpleArrayFloat64 &) -> bool + { return true; }) + // + ; + } + +}; /* end class WrapTestSimpleArrayHelper */ + +void wrap_TestSimpleArrayHelper(pybind11::module & mod) +{ + WrapTestSimpleArrayHelper::commit(mod, "TestSimpleArrayHelper", "TestSimpleArrayHelper"); +} + +} /* end namespace python */ + +} /* end namespace modmesh */ + +// vim: set ff=unix fenc=utf8 et sw=4 ts=4 sts=4: \ No newline at end of file diff --git a/modmesh/core.py b/modmesh/core.py index 10f8440c..74273f18 100644 --- a/modmesh/core.py +++ b/modmesh/core.py @@ -43,6 +43,7 @@ 'time_registry', 'ConcreteBuffer', 'Gmsh', + 'SimpleArray', 'SimpleArrayBool', 'SimpleArrayInt8', 'SimpleArrayInt16', @@ -73,6 +74,9 @@ 'Bezier3dFp64', 'WorldFp32', 'WorldFp64', + + # TODO: have a way to enable this according to `USE_PYTEST_HELPER_BINDING` + 'testhelper', ] diff --git a/tests/test_buffer.py b/tests/test_buffer.py index 7bdeeeab..9d3fc8f6 100644 --- a/tests/test_buffer.py +++ b/tests/test_buffer.py @@ -598,6 +598,71 @@ def test_SimpleArray_broadcast_slice_ghost_md(self): for k in range(4): self.assertEqual(ndarr2[i, j, k], sarr[i - G, j, k]) + def test_SimpleArray_casting(self): + helper = modmesh.testhelper.TestSimpleArrayHelper + # first check the caster works if the argument is exact the same type + array_float64 = modmesh.SimpleArrayFloat64((2, 3, 4)) + self.assertEqual( + helper.test_load_arrayfloat64_from_arrayplex(array_float64), True) + + # init arrayplex + arrayplex_int32 = modmesh.SimpleArray((2, 3, 4), dtype="int32") + arrayplex_uint64 = modmesh.SimpleArray((2, 3, 4), dtype="uint64") + arrayplex_float64 = modmesh.SimpleArray((2, 3, 4), dtype="float64") + + # check the type is the same with different data types + self.assertTrue(type(arrayplex_int32) is type(arrayplex_uint64)) + self.assertTrue(type(arrayplex_uint64) is type(arrayplex_float64)) + self.assertEqual( + str(type(arrayplex_int32)), "") + self.assertEqual( + str(type(arrayplex_uint64)), "") + self.assertEqual( + str(type(arrayplex_float64)), "") + + # check if arrayplex can cast to simplearray + self.assertEqual( + helper.test_load_arrayin32_from_arrayplex(arrayplex_int32), True) + + # int32 and uint64 are different types + with self.assertRaisesRegex( + TypeError, + r"incompatible function arguments"): + helper.test_load_arrayin32_from_arrayplex(arrayplex_uint64) + + # check if arrayplex can cast to simplearray + self.assertEqual( + helper.test_load_arrayfloat64_from_arrayplex(arrayplex_float64), True) # noqa: E501 + + # float64 and int32 are differet types + with self.assertRaisesRegex(TypeError, + r"incompatible function arguments"): + helper.test_load_arrayfloat64_from_arrayplex(arrayplex_int32) + + # explicitly check the `cast` function of the customized caster works + # SimpleArray32 from the constructor directly + array_int32 = modmesh.SimpleArrayInt32((2, 3, 4)) + # SimpleArray32 from casting + array_int32_2 = helper.test_cast_to_arrayint32() + self.assertTrue(type(array_int32) is type(array_int32_2)) + self.assertEqual( + str(type(array_int32)), "") + self.assertEqual( + str(type(array_int32_2)), "") + + def test_SimpleArray_SimpleArrayPlex_type_change(self): + arrayplex_int32 = modmesh.SimpleArray((2, 3, 4), dtype="int32") + + # from plex to typed + array_int32 = arrayplex_int32.typed + self.assertEqual( + str(type(array_int32)), "") + + # from typed to plex + arrayplex_int32_2 = array_int32.plex + self.assertEqual( + str(type(arrayplex_int32_2)), "") + class SimpleArrayCalculatorsTC(unittest.TestCase):