diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2dd146e --- /dev/null +++ b/.clang-format @@ -0,0 +1,92 @@ +--- +Language: Cpp +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: true + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: true +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakConstructorInitializersBeforeComma: false +BreakStringLiterals: true +ColumnLimit: 150 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 2 +IndentPPDirectives: AfterHash +IndentWrappedFunctionNames: true +NamespaceIndentation: Inner +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: false +# SpaceAfterLogicalNot: false # No longer available in clang-format 6.0 +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +# SpaceBeforeCpp11BracedList: true # No longer available in clang-format 6.0 +# SpaceBeforeCtorInitializerColon: true # No longer available in clang-format 6.0 +# SpaceBeforeInheritanceColon: true # No longer available in clang-format 6.0 +SpaceBeforeParens: ControlStatements +# SpaceBeforeRangeBasedForLoopColon: true # No longer available in clang-format 6.0 +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SortIncludes: false +SortUsingDeclarations: false +Standard: Cpp11 +TabWidth: 2 +UseTab: Never +... diff --git a/.gitignore b/.gitignore index 3a6adca..dd458d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build/ +build*/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/CMakeLists.txt b/CMakeLists.txt index a8d8a8f..1d8b830 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,24 @@ -cmake_minimum_required(VERSION 3.18.0) -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -# Do not enable compiler specific extensions, for eg on GCC use -std=c++1z (=c++17) and not -std=gnu++17 -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_VERBOSE_MAKEFILE TRUE) +cmake_minimum_required(VERSION 3.17.5) + +add_library(cpp_warning_options INTERFACE) +add_library(cpp_compile_options INTERFACE) + +target_compile_features(cpp_compile_options INTERFACE cxx_std_17) + +if(MSVC) + target_compile_options(cpp_warning_options INTERFACE /W4 /Werror) +else() + target_compile_options( + cpp_warning_options INTERFACE -Wall -Wextra -Wconversion -Werror) +# target_compile_options(cpp_compile_options +# INTERFACE -fsanitize=undefined,address) +# target_link_options(cpp_compile_options INTERFACE +# -fsanitize=undefined,address) +endif() +# Do not enable compiler specific extensions, for eg on GCC use -std=c++1z +# (=c++17) and not -std=gnu++17 +set(CMAKE_CXX_EXTENSIONS OFF) # Use ccache is available, has to be before "project()" find_program(CCACHE_PROGRAM ccache) @@ -13,59 +27,24 @@ if(CCACHE_PROGRAM) set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}") endif() - project(test_swig VERSION 0.0.1) # Set a default build type if none was specified if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Release' as none was specified.") - set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") + "MinSizeRel" "RelWithDebInfo") endif() -############################################################################### -# N I N J A # -############################################################################### - -# Xcode/Ninja generators undefined MAKE -if(CMAKE_GENERATOR MATCHES "Make") - set(MAKE "$(MAKE)") -else() - set(MAKE make) -endif() - -# Ninja support: has to be atop for it to take effect before anything else is done -# Add Color Output if Using Ninja -macro(AddCXXFlagIfSupported flag test) - CHECK_CXX_COMPILER_FLAG(${flag} ${test}) - if(${${test}}) - message("adding ${flag}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") - endif() -endmacro() - -if("Ninja" STREQUAL ${CMAKE_GENERATOR}) - # Clang - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - AddCXXFlagIfSupported(-fcolor-diagnostics COMPILER_SUPPORTS_fcolor-diagnostics) - endif() - - # g++ - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # For some reason it doesn't say its supported, but it works... - # AddCXXFlagIfSupported(-fdiagnostics-color COMPILER_SUPPORTS_fdiagnostics-color) - message(STATUS "Ninja: Forcing -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") - endif() -endif() - - -# Search first in the binary dir, where conan will install finders, then -# search for modules in the root dir to override cmake ones -# Start with ROOT, then PROJECT_BINARY_DIR -list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +# Search first in the binary dir, where conan will install finders, then search +# for modules in the root dir to override cmake ones Start with ROOT, then +# CMAKE_CURRENT_BINARY_DIR +list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/CMake") include_directories("${CMAKE_CURRENT_BINARY_DIR}") # Add to include path @@ -73,69 +52,56 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}") # Project source directory include_directories("${PROJECT_SOURCE_DIR}") -############################################################################### -# C M A K E C O N T R O L # -############################################################################### +# ############################################################################## +# C M A K E C O N T R O L # +# ############################################################################## -# High level project configuration -# Do we actually want everything to go to CMAKE_BINARY_DIR/Products, -# so that when you build OpenStudioApplication you get both OpenStudio (core) and Application in the same place? -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/Products") +# High level project configuration Do we actually want everything to go to +# CMAKE_BINARY_DIR/Products, so that when you build OpenStudioApplication you +# get both OpenStudio (core) and Application in the same place? +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Products") -set(LIBRARY_SEARCH_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Release" "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Debug") +set(LIBRARY_SEARCH_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}" + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Release" + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Debug") -# Search first in the binary dir, where conan will install finders, then -# search for modules in the root dir to override cmake ones -list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMake") +# Search first in the binary dir, where conan will install finders, then search +# for modules in the root dir to override cmake ones +list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}" + "${PROJECT_SOURCE_DIR}/CMake") -####################################################################### -# R E G U L A R O U P U T A N D L I B S # -####################################################################### +# ############################################################################## +# R E G U L A R O U P U T A N D L I B S # +# ############################################################################## set(target_name mylib) -set(${target_name}_src - Person.hpp - Person.cpp -) - -add_library(${target_name} ${${target_name}_src}) -target_link_libraries(${target_name} - ${${target_name}_depends} -) -# Executable -add_executable(Test - main.cpp -) +set(${target_name}_src Measure.hpp Measure.cpp Model.hpp Model.cpp ModelObject.hpp ModelObject.cpp Runner.hpp + SpecialRunner.hpp) -target_link_libraries(Test - ${target_name} -) - - -####################################################################### -# S W I G # -####################################################################### +add_library(${target_name} SHARED ${${target_name}_src}) +target_link_libraries(${target_name} PRIVATE cpp_warning_options + cpp_compile_options) +# ############################################################################## +# S W I G # +# ############################################################################## option(BUILD_RUBY_BINDINGS "Build Ruby bindings" ON) # Build CSharp bindings option(BUILD_CSHARP_BINDINGS "Build CSharp bindings" OFF) -# Build Python bindings -# Requires: SWIG Python +# Build Python bindings Requires: SWIG Python option(BUILD_PYTHON_BINDINGS "Build Python bindings" ON) - # lib swig files -set(${target_name}_swig_src - Person.i -) +set(${target_name}_swig_src Measure.i) -foreach (SWIG_FILE ${${target_name}_swig_src}) +foreach(SWIG_FILE ${${target_name}_swig_src}) message("SWIG_FILE=${SWIG_FILE}") set_source_files_properties(${SWIG_FILE} PROPERTIES CPLUSPLUS ON) endforeach() @@ -147,101 +113,176 @@ include(${SWIG_USE_FILE}) enable_testing() include(CTest) +if(BUILD_PYTHON_BINDINGS) -if (BUILD_PYTHON_BINDINGS) + add_compile_definitions(WITHPYTHON) set(swig_target_name ${swig_target_name}_python) - find_package(Python COMPONENTS Interpreter Development REQUIRED) + find_package( + Python + COMPONENTS Interpreter Development + REQUIRED) - include_directories(SYSTEM ${Python_INCLUDE_DIRS}) - - swig_add_library(${swig_target_name} + swig_add_library( + ${swig_target_name} TYPE MODULE - LANGUAGE python - OUTPUT_DIR "${PROJECT_BINARY_DIR}/Products/python" - OUTFILE_DIR "${PROJECT_BINARY_DIR}/python_wrapper" - SOURCES ${${target_name}_swig_src} - ) - - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) - - set_target_properties(${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/") - set_target_properties(${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python/") - set_target_properties(${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/python/") - - - swig_link_libraries(${swig_target_name} ${target_name}) - - - if (MSVC) + LANGUAGE python OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/python" + OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" + SOURCES ${${target_name}_swig_src}) + + set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME + ${target_name}) + + set_target_properties( + ${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY + "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/") + set_target_properties( + ${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/python/") + set_target_properties( + ${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/python/") + +# target_include_directories(${swig_target_name} PRIVATE SYSTEM +# ${Python_INCLUDE_DIRS}) + + swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE + cpp_compile_options) + +# if(MSVC) swig_link_libraries(${swig_target_name} PRIVATE Python::Module) - endif() +# endif() add_test( - NAME test_ruby_only - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_ruby_only.rb - ) + NAME test_python_only + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python + COMMAND ${Python_EXECUTABLE} test_python_only.rb) + + # Must call CMake itself in order to set the SWIG_LIB env var for + # add_custom_command + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-python" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") endif() if(BUILD_RUBY_BINDINGS) find_package(Ruby REQUIRED) - include_directories(SYSTEM ${Ruby_INCLUDE_DIRS}) - set(swig_target_name ${target_name}_ruby) - swig_add_library(${swig_target_name} + swig_add_library( + ${swig_target_name} TYPE MODULE - LANGUAGE ruby - OUTPUT_DIR "${PROJECT_BINARY_DIR}/Products/ruby" - OUTFILE_DIR "${PROJECT_BINARY_DIR}/ruby_wrapper" - SOURCES ${${target_name}_swig_src} - ) + LANGUAGE ruby OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/Products/ruby" + OUTFILE_DIR "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" + SOURCES ${${target_name}_swig_src}) - set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME ${target_name}) + set_target_properties(${swig_target_name} PROPERTIES OUTPUT_NAME + ${target_name}) - set_target_properties(${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") - set_target_properties(${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") - set_target_properties(${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") + set_target_properties( + ${swig_target_name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY + "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") + set_target_properties( + ${swig_target_name} PROPERTIES LIBRARY_OUTPUT_DIRECTORY + "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") + set_target_properties( + ${swig_target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") - swig_link_libraries(${swig_target_name} ${target_name} ${Ruby_LIBRARIES}) + target_include_directories( + ${swig_target_name} PRIVATE SYSTEM ${RUBY_INCLUDE_DIRS} ) + + swig_link_libraries(${swig_target_name} PUBLIC ${target_name} PRIVATE + ${RUBY_LIBRARIES} cpp_compile_options) add_test( - NAME test_python_only - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python - COMMAND ${Python_EXECUTABLE} test_python_only.py - ) + NAME test_ruby_only + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby + COMMAND ${RUBY_EXECUTABLE} test_ruby_only.py) -endif() + # Must call CMake itself in order to set the SWIG_LIB env var for + # add_custom_command + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + COMMAND + ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_DIR}" "${SWIG_EXECUTABLE}" "-v" + "-ruby" -external-runtime + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx") + +endif() if(BUILD_RUBY_BINDINGS AND BUILD_PYTHON_BINDINGS) add_test( NAME test_pycall WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall.rb) add_test( NAME test_pycall_workaround WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_workaround.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall_workaround.rb) add_test( NAME test_pycall_use_cpp_to_convert_pointers WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_use_cpp_to_convert_pointers.rb - ) + COMMAND ${RUBY_EXECUTABLE} test_pycall_use_cpp_to_convert_pointers.rb) add_test( NAME test_pycall_script WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/ruby - COMMAND ${Ruby_EXECUTABLE} test_pycall_script.rb + COMMAND ${RUBY_EXECUTABLE} test_pycall_script.rb) + +endif() + +if (BUILD_PYTHON_BINDINGS) + +endif() + + + +if (BUILD_PYTHON_BINDINGS) + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + PythonEngine.hpp + PythonEngine.cpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" + "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper/SWIGPythonRuntime.hxx") + + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options ${RUBY_LIBRARY} Python::Python) + + target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" "${CMAKE_CURRENT_BINARY_DIR}/python_wrapper" + ${RUBY_INCLUDE_DIRS}) +else() + + # Executable + add_executable( + Test + main.cpp + ScriptEngine.hpp + RubyEngine.hpp + RubyEngine.cpp + "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper/SWIGRubyRuntime.hxx" ) + target_link_libraries(Test PRIVATE ${target_name} cpp_compile_options + cpp_warning_options ${RUBY_LIBRARY}) + + target_include_directories(Test PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/ruby_wrapper" + ${RUBY_INCLUDE_DIRS}) + endif() diff --git a/Measure.cpp b/Measure.cpp new file mode 100644 index 0000000..ad91c92 --- /dev/null +++ b/Measure.cpp @@ -0,0 +1,7 @@ +#include "Measure.hpp" + +namespace Test { +bool Measure::run(Runner& runner) { + return run_impl(runner); +} +} // namespace Test \ No newline at end of file diff --git a/Measure.hpp b/Measure.hpp new file mode 100644 index 0000000..30e6694 --- /dev/null +++ b/Measure.hpp @@ -0,0 +1,36 @@ +#ifndef MEASURE_HPP +#define MEASURE_HPP + +#include +#include +#include +#include + +namespace Test { +class Runner; + +class Model; + +class Measure +{ + public: + Measure() = default; + Measure(const Measure&) = delete; + Measure(Measure&&) = delete; + Measure& operator=(const Measure&) = delete; + Measure& operator=(Measure&&) = delete; + virtual ~Measure() = default; + + virtual std::string name() = 0; + bool run(Runner&); + + protected: + // protected virtual here allows us to easily change the internal interface + // and behavior of measure + virtual bool run_impl(Runner&) = 0; +}; + + +} // Namespace Test + +#endif // ifndef MEASURE_HPP diff --git a/Measure.i b/Measure.i new file mode 100644 index 0000000..b21a16d --- /dev/null +++ b/Measure.i @@ -0,0 +1,55 @@ +#ifndef MEASURE_I +#define MEASURE_I + +#include + +%begin %{ + // ... code in begin section ... +%} + +%runtime %{ + // ... code in runtime section ... +%} + +%header %{ + // ... code in header section ... +%} + +%wrapper %{ + // ... code in wrapper section ... +%} + +%init %{ + // ... code in init section ... +%} + +%module mylib +%module(directors="1") mylib + + + +%include +%include +%include + +%feature("director") Measure; + +#if defined(SWIGPYTHON) +%rename (PythonMeasure) Test::Measure; +//%rename ("Python%s") ""; +#endif + +%{ + #include + #include + #include + #include +%} + +%include +%include +%include +%include + +#endif //MEASURE_I + diff --git a/Model.cpp b/Model.cpp new file mode 100644 index 0000000..e65aec3 --- /dev/null +++ b/Model.cpp @@ -0,0 +1,44 @@ +#include "Model.hpp" +#include "ModelObject.hpp" + +namespace Test { + Model::Model(std::string name) : name_(std::move(name)) {} + + const std::string& Model::getName() const { + return name_; + } + + bool Model::setName(const std::string& name) { + name_ = name; + return true; + } + + void Model::pushOp(const std::string& op_name) { + opsPerformed_.push_back(op_name); + } + + const std::vector& Model::opsPerformed() const { + return opsPerformed_; + } + + int Model::numObjects() const { + return static_cast(m_objects.size()); + } + + std::vector Model::objectNames() const { + std::vector result; + for (auto objPtr : m_objects) { + result.push_back(objPtr.getName()); + } + return result; + } + + ModelObject& Model::getObject(size_t index) { + return m_objects[index]; + } + + void Model::pushObject(const std::string& objName) { + m_objects.emplace_back(objName); + } + +} // namespace Test diff --git a/Model.hpp b/Model.hpp new file mode 100644 index 0000000..df81973 --- /dev/null +++ b/Model.hpp @@ -0,0 +1,31 @@ +#ifndef MODEL_HPP +#define MODEL_HPP + +#include +#include +#include "ModelObject.hpp" + +namespace Test { + class Model + { + public: + explicit Model(std::string name); + const std::string& getName() const; + bool setName(const std::string& name); + + void pushOp(const std::string& op_name); + + const std::vector& opsPerformed() const; + + int numObjects() const; + std::vector objectNames() const; + ModelObject& getObject(size_t index); + void pushObject(const std::string& objName); + private: + std::string name_; + std::vector opsPerformed_; + std::vector m_objects; + }; +} // namespace Test + +#endif diff --git a/ModelObject.cpp b/ModelObject.cpp new file mode 100644 index 0000000..0c64261 --- /dev/null +++ b/ModelObject.cpp @@ -0,0 +1,19 @@ +#include "ModelObject.hpp" + +namespace Test { + + ModelObject::ModelObject(std::string name) + : name_(std::move(name)) + { + } + + const std::string& ModelObject::getName() const { + return name_; + } + + bool ModelObject::setName(const std::string& name) { + name_ = name; + return true; + } + +} // namespace Test diff --git a/ModelObject.hpp b/ModelObject.hpp new file mode 100644 index 0000000..b4db4b8 --- /dev/null +++ b/ModelObject.hpp @@ -0,0 +1,19 @@ +#ifndef MODELOBJECT_HPP +#define MODELOBJECT_HPP + +#include + +namespace Test { + + class ModelObject { + public: + explicit ModelObject(std::string name); + const std::string& getName() const; + bool setName(const std::string& name); + + private: + std::string name_; + }; +} // namespace Test + +#endif diff --git a/Person.cpp b/Person.cpp deleted file mode 100644 index 0a97770..0000000 --- a/Person.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "Person.hpp" - -#include - -namespace Test { - -Person::Person(const std::string& t_name) noexcept - : m_name(t_name) { } - -Person::Person(const Person& other) - : m_name(other.m_name) { } - - -std::string Person::getName() const { - return m_name; -} - -bool Person::setName(const std::string& t_newName) { - m_name = t_newName; - return true; -} - -std::ostream& operator<<(std::ostream& os, const Test::Person& p) { - os << "Person named '" << p.getName() << "'"; - return os; -} - -std::string personName(const Person& person) { - return person.getName(); -} - -} // namespace Test diff --git a/Person.hpp b/Person.hpp deleted file mode 100644 index f89f612..0000000 --- a/Person.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef EXAMPLE_HPP -#define EXAMPLE_HPP - -#include -#include -#include - -namespace Test { - -class Person { - public: - - Person(const std::string& name) noexcept; - - Person(const Person& other); - - std::string getName() const; - bool setName(const std::string& t_newName); - - private: - std::string m_name; -}; - -std::ostream& operator<<(std::ostream&, const Test::Person&); - -// A free-standing function taking an object as argument -std::string personName(const Person& person); -inline void setName(Person &p, const std::string &newname) { - p.setName(newname); -} - -// get an integral representation of the pointer that is this Person -inline long long toInt(Person &p) { - std::clog << "original pointer: " << &p << '\n'; - const auto result = reinterpret_cast(&p); - std::clog << "toInt from C++ " << result << '\n'; - return result; -} - -// take the integer from toInt and reinterpret_cast it back into a Person *, then return that as a reference -inline Person &fromInt(long long i) { - auto *ptr = reinterpret_cast(i); - std::clog << "Reclaimed pointer: " << ptr << '\n'; - return *ptr; -} - -} // Namespace Test - -#endif // ifndef PERSON_HPP diff --git a/Person.i b/Person.i deleted file mode 100644 index ff1ad08..0000000 --- a/Person.i +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef PERSON_I -#define PERSON_I - -%module mylib - -%include -%include - -%{ - #include - #include -%} - -%include - -%extend Test::Person { - // Use the overloaded operator<< for string representation - std::string __str__() { - std::ostringstream os; - os << *$self; - return os.str(); - } - #ifdef SWIGRUBY - // get an integral representation of the pointer that is this Person - inline long long __toInt() { - std::clog << "original pointer: " << $self << '\n'; - const auto result = reinterpret_cast($self); - std::clog << "toInt from C++ " << result << '\n'; - return result; - } - - - #endif - - #ifdef SWIGPYTHON - // take the integer from toInt and reinterpret_cast it back into a Person *, then return that as a reference - static inline Test::Person& _fromInt(long long i) { - auto *ptr = reinterpret_cast(i); - std::clog << "Reclaimed pointer: " << ptr << '\n'; - return *ptr; - } - #endif -}; - -#endif //PERSON_I - diff --git a/PythonEngine.cpp b/PythonEngine.cpp new file mode 100644 index 0000000..c58a0a7 --- /dev/null +++ b/PythonEngine.cpp @@ -0,0 +1,158 @@ +#include "PythonEngine.hpp" + +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +#include +#include "SWIGPythonRuntime.hxx" + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +namespace Test { + +PythonEngine::PythonEngine([[maybe_unused]] const int argc, const char* argv[]) : program(Py_DecodeLocale(argv[0], nullptr)) { + if (program == nullptr) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + + Py_SetProgramName(program); // optional but recommended + Py_Initialize(); +} + +PythonEngine::~PythonEngine() { + if (Py_FinalizeEx() < 0) { + exit(120); + } + PyMem_RawFree(program); +} + +struct PythonObject { + PythonObject() = default; + + PythonObject(PyObject *obj) noexcept : obj_(obj) + { + if (obj_) { + Py_INCREF(obj_); + } + } + + PythonObject(const PythonObject &other) noexcept + : obj_(other.obj_) { + if (obj_) { + Py_INCREF(obj_); + } + } + + PythonObject(PythonObject &&other) noexcept + : obj_(other.obj_) { + // no reason to inc/dec, we just stole the ref counted object + // from other + other.obj_ = nullptr; + } + + PythonObject &operator=(const PythonObject &rhs) noexcept { + if (&rhs != this) { + obj_ = rhs.obj_; + + if (obj_) { + Py_INCREF(obj_); + } + } + + return *this; + } + + PythonObject &operator=(PythonObject &&rhs) noexcept { + if (&rhs != this) { + obj_ = rhs.obj_; + rhs.obj_ = nullptr; + } + + return *this; + } + + ~PythonObject() { + if (obj_) { + Py_DECREF(obj_); + } + } + + PyObject *obj_ = nullptr; +}; + +void PythonEngine::exec(std::string_view sv) { + std::string command{sv}; + + PyObject* m = PyImport_AddModule("__main__"); + if (m == nullptr) { + throw std::runtime_error("Unable to add module __main__ for python script execution"); + } + + PyObject* d = PyModule_GetDict(m); + PyObject* v = PyRun_String(command.c_str(), Py_file_input, d, d); + if (v == nullptr) { + PyErr_Print(); + throw std::runtime_error("Error executing Python code"); + } + + //decref count returned from Python + Py_DECREF(v); +} + +ScriptObject PythonEngine::eval(std::string_view sv) { + std::string command{sv}; + + PyObject* m = PyImport_AddModule("__main__"); + if (m == nullptr) { + throw std::runtime_error("Unable to add module __main__ for python script execution"); + } + + PyObject* d = PyModule_GetDict(m); + PyObject* v = PyRun_String(command.c_str(), Py_eval_input, d, d); + if (v == nullptr) { + PyErr_Print(); + throw std::runtime_error("Error executing Python code"); + } + + //share in ownership + PythonObject return_value(v); + + //decref count returned from Python + Py_DECREF(v); + + return ScriptObject{return_value}; +} + +// convert the underlying object to the correct type, then return it as a void * +// so the above template function can provide it back to the caller. +void* PythonEngine::getAs_impl(ScriptObject& obj, const std::type_info& ti) { + + auto val = std::any_cast(obj.object); + + const auto& type_name = getRegisteredTypeName(ti); + + void* return_value = nullptr; + + auto* type = SWIG_Python_TypeQuery(type_name.c_str()); + + if (!type) { + throw std::runtime_error("Unable to find type in SWIG"); + } + + const auto result = SWIG_Python_ConvertPtr(val.obj_, &return_value, type, 0); + + if (!SWIG_IsOK(result)) { + throw std::runtime_error("Error getting object from SWIG/Python"); + } + + return return_value; +} +} // namespace Test diff --git a/PythonEngine.hpp b/PythonEngine.hpp new file mode 100644 index 0000000..15f4dc5 --- /dev/null +++ b/PythonEngine.hpp @@ -0,0 +1,31 @@ +#ifndef PYTHONENGINE_included +#define PYTHONENGINE_included + +#include "ScriptEngine.hpp" + +namespace Test { +class PythonEngine final : public ScriptEngine +{ + public: + PythonEngine(const int argc, const char* argv[]); + ~PythonEngine() override; + + PythonEngine(const PythonEngine&) = delete; + PythonEngine(PythonEngine&&) = delete; + PythonEngine& operator=(const PythonEngine&) = delete; + PythonEngine& operator=(PythonEngine&&) = delete; + + ScriptObject eval(std::string_view sv) override; + void exec(std::string_view sv) override; + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + void* getAs_impl(ScriptObject& obj, const std::type_info&) override; + + private: + wchar_t* program; +}; +} // namespace Test + +#endif diff --git a/RubyEngine.cpp b/RubyEngine.cpp new file mode 100644 index 0000000..a1a4203 --- /dev/null +++ b/RubyEngine.cpp @@ -0,0 +1,84 @@ +#include "RubyEngine.hpp" + +#include +#include + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wregister" +# pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#include "SWIGRubyRuntime.hxx" + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +namespace Test { + +RubyEngine::RubyEngine() { + ruby_setup(); +} + +RubyEngine::~RubyEngine() { + ruby_finalize(); +} + +static VALUE evaluateSimpleImpl(VALUE arg) { + return rb_eval_string(StringValuePtr(arg)); +} + +VALUE evalString(const std::string& t_str) { + VALUE val = rb_str_new2(t_str.c_str()); + int error; + // save and restore the current working directory in case the call to ruby upsets it + VALUE result = rb_protect(evaluateSimpleImpl, val, &error); + if (error != 0) { + VALUE errval = rb_eval_string("$!.to_s"); + char* str = StringValuePtr(errval); + std::string err(str); + VALUE locval = rb_eval_string("$@.to_s"); + str = StringValuePtr(locval); + std::string loc(str); + throw std::runtime_error("Error '" + err + "' at " + loc); + } + + return result; +} + +ScriptObject RubyEngine::eval(std::string_view sv) { + std::string str{sv}; + return ScriptObject{evalString(str)}; +} + +void RubyEngine::exec(std::string_view sv) { + std::string str{sv}; + [[maybe_unused]] const auto result = evalString(str); +} + +// convert the underlying object to the correct type, then return it as a void * +// so the above template function can provide it back to the caller. +void* RubyEngine::getAs_impl(ScriptObject& obj, const std::type_info &ti) { + auto val = std::any_cast(obj.object); + + const auto &type_name = getRegisteredTypeName(ti); + + void *return_value = nullptr; + + auto *type = SWIG_TypeQuery(type_name.c_str()); + + if (!type) { + throw std::runtime_error("Unable to find type in SWIG"); + } + + const auto result = SWIG_ConvertPtr(val, &return_value, type, 0); + + if (!SWIG_IsOK(result)) { + throw std::runtime_error("Error getting object from SWIG/Ruby"); + } + + return return_value; +} + +} // namespace Test diff --git a/RubyEngine.hpp b/RubyEngine.hpp new file mode 100644 index 0000000..8eee703 --- /dev/null +++ b/RubyEngine.hpp @@ -0,0 +1,28 @@ +#ifndef RUBYENGINE_included +#define RUBYENGINE_included + +#include "ScriptEngine.hpp" + +namespace Test { +class RubyEngine final : public ScriptEngine +{ + public: + RubyEngine(); + ~RubyEngine() override; + + RubyEngine(const RubyEngine&) = delete; + RubyEngine(RubyEngine&&) = delete; + RubyEngine& operator=(const RubyEngine&) = delete; + RubyEngine& operator=(RubyEngine&&) = delete; + + ScriptObject eval(std::string_view sv) override; + void exec(std::string_view sv) override; + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + void* getAs_impl(ScriptObject& obj, const std::type_info&) override; +}; +} // namespace Test + +#endif diff --git a/Runner.hpp b/Runner.hpp new file mode 100644 index 0000000..824a67c --- /dev/null +++ b/Runner.hpp @@ -0,0 +1,26 @@ +#ifndef TEST_SWIG_RUNNER_HPP +#define TEST_SWIG_RUNNER_HPP + +namespace Test { +class Model; + +class Runner +{ + public: + virtual ~Runner() = default; + Runner &operator=(Runner &&) = delete; + Runner &operator=(const Runner &) = delete; + Runner(Runner &&) = delete; + Runner(const Runner &) = delete; + Runner() = default; + + Model &get_current_model() { return get_current_model_impl(); } + + protected: + + virtual Model& get_current_model_impl() = 0; + +}; +} // namespace Test + +#endif //TEST_SWIG_RUNNER_HPP diff --git a/ScriptEngine.hpp b/ScriptEngine.hpp new file mode 100644 index 0000000..85c9fb6 --- /dev/null +++ b/ScriptEngine.hpp @@ -0,0 +1,76 @@ +#ifndef SCRIPTENGINE_included +#define SCRIPTENGINE_included + +#include +#include +#include +#include +#include +#include +#include + +namespace Test { +struct ScriptObject +{ + std::any object; +}; + +class ScriptEngine +{ + public: + ScriptEngine() = default; + virtual ~ScriptEngine() = default; + ScriptEngine(const ScriptEngine&) = delete; + ScriptEngine(ScriptEngine&&) = delete; + ScriptEngine& operator=(const ScriptEngine&) = delete; + ScriptEngine& operator=(ScriptEngine&&) = delete; + + virtual ScriptObject eval(std::string_view sv) = 0; + + // execute string without expecting a return value + virtual void exec(std::string_view sv) = 0; + + template + T getAs(ScriptObject& obj) { + void* result = getAs_impl(obj, typeid(T)); + if (result) { + return static_cast(result); + } else { + throw std::bad_cast(); + } + } + + template + void registerType(std::string name) { + types.emplace(std::cref(typeid(T)), std::move(name)); + } + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + virtual void* getAs_impl(ScriptObject& obj, const std::type_info&) = 0; + + const std::string& getRegisteredTypeName(const std::type_info& type) { + const auto& found_name = types.find(type); + + if (found_name != types.end()) { + return found_name->second; + } + + throw std::runtime_error("unknown type requested"); + } + + private: + struct Compare + { + bool operator()(const std::reference_wrapper &lhs, const std::reference_wrapper& rhs) const { + return lhs.get().before(rhs.get()); + } + }; + + std::map, std::string, Compare> types; +}; + +} // namespace Test + +#endif diff --git a/SpecialRunner.hpp b/SpecialRunner.hpp new file mode 100644 index 0000000..b706fe8 --- /dev/null +++ b/SpecialRunner.hpp @@ -0,0 +1,24 @@ +#ifndef TEST_SWIG_SPECIALRUNNER_HPP +#define TEST_SWIG_SPECIALRUNNER_HPP + +#include "Runner.hpp" +#include "Model.hpp" + +namespace Test { +// a concrete implementation of the Runner class, for testing purposes +class SpecialRunner : public Runner +{ + public: + SpecialRunner(Model model) : model_(std::move(model)) {} + + protected: + Model& get_current_model_impl() override { + return model_; + } + + private: + Model model_; +}; +} // namespace Test + +#endif //TEST_SWIG_SPECIALRUNNER_HPP diff --git a/main.cpp b/main.cpp index 8c17240..6aa550c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,99 @@ -#include "Person.hpp" +// CMake defines WITHPYTHON (or not) + #include +#include +#include "RubyEngine.hpp" + +#ifdef WITHPYTHON +#include "PythonEngine.hpp" +#endif + +#include "Measure.hpp" +#include "SpecialRunner.hpp" + +#include "Model.hpp" + +int main([[maybe_unused]] const int argc, [[maybe_unused]] const char* argv[]) { + Test::RubyEngine ruby; + #ifdef WITHPYTHON + Test::PythonEngine python{argc, argv}; + #endif + + ruby.exec(R"(puts("Hello from Ruby"))"); + + #ifdef WITHPYTHON + python.exec(R"(print("Hello From Python"))"); + #endif + + ruby.registerType("Test::Measure *"); + + #ifdef WITHPYTHON + python.registerType("Test::Measure *"); + #endif + + ruby.exec("require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/ruby/test_measure.rb'"); + auto ruby_measure = ruby.eval("RubyTestMeasure.new()"); + auto* ruby_measure_from_cpp = ruby.getAs(ruby_measure); + assert(ruby_measure_from_cpp); + std::cout << "Ruby measure name: " << ruby_measure_from_cpp->name() << '\n'; + + #ifdef WITHPYTHON + python.exec("import sys\nsys.path.append('/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/python/')"); + python.exec("import test_measure"); + auto python_measure = python.eval("test_measure.PythonTestMeasure()"); + auto* python_measure_from_cpp = python.getAs(python_measure); + assert(python_measure_from_cpp); + #endif + + // this all works up until this moment right here. + // At this point it doesn't call the SWIG Director for Python + // It actually calls the SWIG Director for Ruby. + // Why? Because we've broken the One Definition Rule. + // SWIG defines functions with the same names for both the Python bindings and the Ruby bindings. + // whichever one gets loaded first wins. + // + // Can we work around this? Maybe. But having both Ruby and Python linked into the same application + // is asking for many problems. + #ifdef WITHPYTHON + std::cout << "Python measure name: " << python_measure_from_cpp->name() << '\n'; + #endif + + auto printObjectNames = [](Test::Model& m) { + for (int i = 0; i < m.numObjects(); ++i) { + std::cout << " * " << i <<" = " << m.getObject(i).getName() << '\n'; + } + }; + + Test::Model m{"C++ Model"}; + m.pushObject("C++ object"); + Test::SpecialRunner sr(m); + std::cout << "Starting out with a Model in C++ called: " << sr.get_current_model().getName() << '\n'; + std::cout << "C++: starting with " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); + + std::cout << "\n\n========== START RUBY ==========\n"; + + ruby_measure_from_cpp->run(sr); + + std::cout << "========== FINISHED RUBY ==========\n\n"; + std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); + + #ifdef WITHPYTHON + std::cout << "\n\n========== START PYTHON ==========\n"; + python_measure_from_cpp->run(sr); + std::cout << "========== FINISHED PYTHON ==========\n\n"; + std::cout << "After Running Ruby and Python: model is named " << sr.get_current_model().getName() << '\n'; + #else + std::cout << "\n\n########## PYTHON ISN'T ENABLED ##########\n\n"; + std::cout << "After Running Ruby only: model is named " << sr.get_current_model().getName() << '\n'; + #endif + + for (const auto& op : sr.get_current_model().opsPerformed()) { + std::cout << "Op 'run' from script: " << op << '\n'; + } + + std::cout << "C++: " << sr.get_current_model().numObjects() << " objects\n"; + printObjectNames(sr.get_current_model()); -int main() { - Test::Person p("John Doe"); - std::cout << p << '\n'; } diff --git a/python/measure.py b/python/measure.py deleted file mode 100644 index a2ad292..0000000 --- a/python/measure.py +++ /dev/null @@ -1,9 +0,0 @@ -import mylib - -class PythonMeasureName: - def run(self, p: mylib.Person, name: str): - print(f"Python script.py: {p.getName()}") - print(f"Changing name to {name}") - p.setName(name) - print(f"Python script.py: {p.getName()}") - return True diff --git a/python/test_measure.py b/python/test_measure.py new file mode 100644 index 0000000..fa49215 --- /dev/null +++ b/python/test_measure.py @@ -0,0 +1,33 @@ + +import sys + +sys.path.append("/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/python/") + +import mylib + +class PythonTestMeasure(mylib.PythonMeasure): + def __init__(self): + mylib.PythonMeasure.__init__(self) + print("Created Object (Python __init__)") + + def run_impl(self, r: mylib.Runner): + m = r.get_current_model() + print(f"Python Model named: {m.getName()}") + m.pushOp("Op from Python") + m.setName("Python Model") + print(f"Python: {m.numObjects()} objects") + m.pushObject("Python Space") + + for i in range(m.numObjects()): + print(f" * {i} = {m.getObject(i).getName()}") + + m.getObject(1).setName("MODIFIED FROM PYTHON") + + return True + def name(self): + return "Python Test Measure" + + +def make_measure(): + return PythonTestMeasure() + diff --git a/python/test_python_only.py b/python/test_python_only.py deleted file mode 100644 index e58e2a0..0000000 --- a/python/test_python_only.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -from pathlib import Path -#import importlib.util - -python_lib_path = Path('../build/Products/python/mylib.py').resolve() - -if not python_lib_path.exists(): - print("Error, this assumes you already built the python bindings project in /build") - raise IOError(f"Could Not find {python_lib_path}") - -# spec = importlib.util.spec_from_file_location("person", - # str(python_lib_path)) -# person = importlib.util.module_from_spec(spec) -# spec.loader.exec_module(person) -sys.path.insert(0, str(python_lib_path.parent)) -import mylib - -def get_a_python_person(): - p = mylib.Person("John") - return p - -def print_a_person(p: mylib.Person): - return mylib.personName(p) - - -if __name__ == '__main__': - p = mylib.Person("John") - print(p) - print(f"{mylib.personName(p)=}") diff --git a/ruby/test_measure.rb b/ruby/test_measure.rb new file mode 100644 index 0000000..a5794f3 --- /dev/null +++ b/ruby/test_measure.rb @@ -0,0 +1,26 @@ +require '/home/julien/Software/Cpp_Swig_Ruby_Python_MCVE/build-modif/Products/ruby/mylib.so' + +class RubyTestMeasure < Mylib::Measure + def name + return "RubyTestMeasure" + end + + def run_impl(runner) + m = runner.get_current_model() + puts "Ruby Model named: #{m.getName()}" + m.setName("Ruby Model") + m.pushOp("A Ruby Op") + + puts "Ruby: Model has: #{m.numObjects()} spaces" + m.pushObject("Ruby Space") + m.numObjects().times do |i| + puts "* #{i} = #{m.getObject(i).getName()}" + end + + # This crashes + m.getObject(0).setName("MODIFIED FROM RUBY") + return true; + end +end + +