├── .github └── workflows │ └── testing.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── CMakePresets.json ├── CONTRIBUTORS.md ├── COPYING ├── FindRuby.cmake ├── Gemfile ├── README.md ├── Rakefile ├── doc ├── Makefile ├── api.rst ├── api │ └── overview.rst ├── architecture.rst ├── architecture │ └── enumerators.rst ├── bindings.rst ├── bindings │ ├── attributes.rst │ ├── buffers.rst │ ├── callbacks.rst │ ├── class_templates.rst │ ├── constants.rst │ ├── constructors.rst │ ├── enums.rst │ ├── exceptions.rst │ ├── inheritance.rst │ ├── instance_registry.rst │ ├── iterators.rst │ ├── memory_management.rst │ ├── methods.rst │ ├── operators.rst │ ├── overloaded_methods.rst │ ├── type_conversions.rst │ ├── type_verification.rst │ └── types_overview.rst ├── conf.py ├── history.rst ├── index.rst ├── introduction.rst ├── make.bat ├── migration.rst ├── packaging.rst ├── packaging │ ├── cmake.rst │ ├── extconf.rb.rst │ └── packaging.rst ├── stl.rst ├── stl │ ├── complex.rst │ ├── exception.rst │ ├── exception_ptr.rst │ ├── map.rst │ ├── multimap.rst │ ├── optional.rst │ ├── pair.rst │ ├── reference_wrapper.rst │ ├── set.rst │ ├── smart_pointers.rst │ ├── stl.rst │ ├── string.rst │ ├── string_view.rst │ ├── tuple.rst │ ├── type_index.rst │ ├── type_info.rst │ ├── unordered_map.rst │ ├── variant.rst │ └── vector.rst ├── tutorial.rst └── why_rice.rst ├── include └── rice │ ├── rice.hpp │ └── stl.hpp ├── lib ├── make_rice_headers.rb ├── mkmf-rice.rb ├── rice.rb ├── rice │ └── version.rb ├── rubygems │ ├── builder.rb │ └── cmake_builder.rb └── rubygems_plugin.rb ├── rice.gemspec ├── rice ├── Address_Registration_Guard.hpp ├── Address_Registration_Guard.ipp ├── Arg.hpp ├── Arg.ipp ├── Buffer.hpp ├── Buffer.ipp ├── Callback.hpp ├── Callback.ipp ├── Constructor.hpp ├── Constructor.ipp ├── Data_Object.hpp ├── Data_Object.ipp ├── Data_Type.hpp ├── Data_Type.ipp ├── Director.hpp ├── Enum.hpp ├── Enum.ipp ├── Exception.hpp ├── Exception.ipp ├── Init.hpp ├── Init.ipp ├── JumpException.hpp ├── JumpException.ipp ├── MemoryView.hpp ├── MemoryView.ipp ├── Return.hpp ├── Return.ipp ├── cpp_api │ ├── Array.hpp │ ├── Array.ipp │ ├── Builtin_Object.hpp │ ├── Builtin_Object.ipp │ ├── Class.hpp │ ├── Class.ipp │ ├── Encoding.hpp │ ├── Encoding.ipp │ ├── Hash.hpp │ ├── Hash.ipp │ ├── Identifier.hpp │ ├── Identifier.ipp │ ├── Module.hpp │ ├── Module.ipp │ ├── Object.hpp │ ├── Object.ipp │ ├── String.hpp │ ├── String.ipp │ ├── Struct.hpp │ ├── Struct.ipp │ ├── Symbol.hpp │ ├── Symbol.ipp │ └── shared_methods.hpp ├── detail │ ├── DefaultHandler.hpp │ ├── DefaultHandler.ipp │ ├── HandlerRegistry.hpp │ ├── HandlerRegistry.ipp │ ├── InstanceRegistry.hpp │ ├── InstanceRegistry.ipp │ ├── MethodInfo.hpp │ ├── MethodInfo.ipp │ ├── Native.hpp │ ├── Native.ipp │ ├── NativeAttributeGet.hpp │ ├── NativeAttributeGet.ipp │ ├── NativeAttributeSet.hpp │ ├── NativeAttributeSet.ipp │ ├── NativeCallbackFFI.hpp │ ├── NativeCallbackFFI.ipp │ ├── NativeCallbackSimple.hpp │ ├── NativeCallbackSimple.ipp │ ├── NativeFunction.hpp │ ├── NativeFunction.ipp │ ├── NativeIterator.hpp │ ├── NativeIterator.ipp │ ├── NativeRegistry.hpp │ ├── NativeRegistry.ipp │ ├── Proc.hpp │ ├── Proc.ipp │ ├── Registries.hpp │ ├── Registries.ipp │ ├── RubyFunction.hpp │ ├── RubyFunction.ipp │ ├── RubyType.hpp │ ├── RubyType.ipp │ ├── Type.hpp │ ├── Type.ipp │ ├── TypeRegistry.hpp │ ├── TypeRegistry.ipp │ ├── Wrapper.hpp │ ├── Wrapper.ipp │ ├── cpp_protect.hpp │ ├── default_allocation_func.hpp │ ├── default_allocation_func.ipp │ ├── from_ruby.hpp │ ├── from_ruby.ipp │ ├── ruby.hpp │ ├── to_ruby.hpp │ └── to_ruby.ipp ├── forward_declares.ipp ├── global_function.hpp ├── global_function.ipp ├── libc │ ├── file.hpp │ └── file.ipp ├── rice.hpp ├── ruby_mark.hpp ├── stl.hpp ├── stl │ ├── complex.hpp │ ├── complex.ipp │ ├── exception.hpp │ ├── exception.ipp │ ├── exception_ptr.hpp │ ├── exception_ptr.ipp │ ├── map.hpp │ ├── map.ipp │ ├── monostate.hpp │ ├── monostate.ipp │ ├── multimap.hpp │ ├── multimap.ipp │ ├── optional.hpp │ ├── optional.ipp │ ├── pair.hpp │ ├── pair.ipp │ ├── reference_wrapper.hpp │ ├── reference_wrapper.ipp │ ├── set.hpp │ ├── set.ipp │ ├── shared_ptr.hpp │ ├── shared_ptr.ipp │ ├── string.hpp │ ├── string.ipp │ ├── string_view.hpp │ ├── string_view.ipp │ ├── tuple.hpp │ ├── tuple.ipp │ ├── type_index.hpp │ ├── type_index.ipp │ ├── type_info.hpp │ ├── type_info.ipp │ ├── unique_ptr.hpp │ ├── unique_ptr.ipp │ ├── unordered_map.hpp │ ├── unordered_map.ipp │ ├── variant.hpp │ ├── variant.ipp │ ├── vector.hpp │ └── vector.ipp └── traits │ ├── attribute_traits.hpp │ ├── function_traits.hpp │ ├── method_traits.hpp │ └── rice_traits.hpp ├── sample ├── callbacks │ ├── CMakeLists.txt │ ├── extconf.rb │ ├── sample_callbacks.cpp │ └── test.rb ├── enum │ ├── CMakeLists.txt │ ├── extconf.rb │ ├── sample_enum.cpp │ └── test.rb ├── inheritance │ ├── CMakeLists.txt │ ├── animals.cpp │ ├── extconf.rb │ └── test.rb └── map │ ├── CMakeLists.txt │ ├── extconf.rb │ ├── map.cpp │ └── test.rb └── test ├── .gitignore ├── CMakeLists.txt ├── embed_ruby.cpp ├── embed_ruby.hpp ├── ext ├── t1 │ ├── CMakeLists.txt │ ├── Foo.hpp │ ├── extconf.rb │ └── t1.cpp └── t2 │ ├── CMakeLists.txt │ ├── extconf.rb │ └── t2.cpp ├── extconf.rb ├── ruby ├── test_callbacks_sample.rb ├── test_multiple_extensions.rb ├── test_multiple_extensions_same_class.rb └── test_multiple_extensions_with_inheritance.rb ├── test_Address_Registration_Guard.cpp ├── test_Array.cpp ├── test_Attribute.cpp ├── test_Buffer.cpp ├── test_Builtin_Object.cpp ├── test_Callback.cpp ├── test_Class.cpp ├── test_Constructor.cpp ├── test_Data_Object.cpp ├── test_Data_Type.cpp ├── test_Director.cpp ├── test_Enum.cpp ├── test_Exception.cpp ├── test_File.cpp ├── test_From_Ruby.cpp ├── test_Hash.cpp ├── test_Identifier.cpp ├── test_Inheritance.cpp ├── test_Iterator.cpp ├── test_Jump_Exception.cpp ├── test_Keep_Alive.cpp ├── test_Keep_Alive_No_Wrapper.cpp ├── test_Memory_Management.cpp ├── test_Module.cpp ├── test_Native_Registry.cpp ├── test_Object.cpp ├── test_Overloads.cpp ├── test_Ownership.cpp ├── test_Proc.cpp ├── test_Self.cpp ├── test_Stl_Exception.cpp ├── test_Stl_Map.cpp ├── test_Stl_Multimap.cpp ├── test_Stl_Optional.cpp ├── test_Stl_Pair.cpp ├── test_Stl_Reference_Wrapper.cpp ├── test_Stl_Set.cpp ├── test_Stl_SharedPtr.cpp ├── test_Stl_String.cpp ├── test_Stl_String_View.cpp ├── test_Stl_Tuple.cpp ├── test_Stl_Type.cpp ├── test_Stl_UniquePtr.cpp ├── test_Stl_Unordered_Map.cpp ├── test_Stl_Variant.cpp ├── test_Stl_Vector.cpp ├── test_String.cpp ├── test_Struct.cpp ├── test_Symbol.cpp ├── test_Template.cpp ├── test_To_Ruby.cpp ├── test_Tracking.cpp ├── test_Type.cpp ├── test_global_functions.cpp ├── unittest.cpp └── unittest.hpp /.github/workflows/testing.yml: -------------------------------------------------------------------------------- 1 | name: Rice CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | tests: 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest, macos-latest, windows-2025] 15 | ruby: ['3.1', '3.2', '3.3', '3.4'] 16 | exclude: 17 | # There's something wrong with this setup in GHA such that 18 | # it gets weird linking errors, however I'm unable to reproduce 19 | # locally so I think it's an infra fluke on GitHub's side. 20 | - os: macos-latest 21 | ruby: '3.1' 22 | runs-on: ${{ matrix.os }} 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically 30 | - name: Configure 31 | run: rake headers 32 | - name: Build and test 33 | run: rake test 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | 3 | # Build artifacts 4 | *.bundle 5 | *.def 6 | *.exe 7 | *.exp 8 | *.ilk 9 | *.in 10 | *.lib 11 | *.o 12 | *.obj 13 | *.pdb 14 | pkg 15 | *.so 16 | 17 | *.log 18 | .*.swp 19 | doc/_build 20 | mkinstalldirs 21 | out 22 | sample/**/Makefile 23 | test/**/Makefile 24 | README.doxygen 25 | 26 | # Various IDE folders 27 | .idea 28 | .vs 29 | msvc 30 | .vscode 31 | .cmake 32 | cmake-build-mingw 33 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.26) 2 | 3 | project(Rice) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | # For older versions of CMake can use include("./FindRuby.cmake") 9 | find_package("Ruby") 10 | 11 | if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 12 | set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$,$>,$<$:EditAndContinue>,$<$:ProgramDatabase>>") 13 | add_compile_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) 14 | add_compile_options(/bigobj /utf-8) 15 | # The default of /EHsc crashes Ruby when calling longjmp with optimizations on (/O2) 16 | string(REGEX REPLACE "/EHsc" "/EHs" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 17 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") 18 | add_compile_definitions(-D_CRT_SECURE_NO_WARNINGS) 19 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 20 | add_compile_options(-ftemplate-backtrace-limit=0) 21 | # https://github.com/doxygen/doxygen/issues/9269#issuecomment-1094975328 22 | #add_compile_options(unittest PRIVATE -Wa,-mbig-obj) 23 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") 24 | endif () 25 | 26 | # Include sub-projects. 27 | add_subdirectory ("test") 28 | add_subdirectory ("sample/callbacks") 29 | add_subdirectory ("sample/enum") 30 | add_subdirectory ("sample/inheritance") 31 | add_subdirectory ("sample/map") 32 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "configurePresets": [ 4 | { 5 | "name": "windows-base", 6 | "hidden": true, 7 | "generator": "Ninja", 8 | "binaryDir": "${sourceDir}/out/build/${presetName}", 9 | "installDir": "${sourceDir}/out/install/${presetName}", 10 | "cacheVariables": { 11 | "CMAKE_C_COMPILER": "cl.exe", 12 | "CMAKE_CXX_COMPILER": "cl.exe" 13 | }, 14 | "condition": { 15 | "type": "equals", 16 | "lhs": "${hostSystemName}", 17 | "rhs": "Windows" 18 | } 19 | }, 20 | { 21 | "name": "x64-debug", 22 | "displayName": "MSVC++ Debug", 23 | "inherits": "windows-base", 24 | "architecture": { 25 | "value": "x64", 26 | "strategy": "external" 27 | }, 28 | "cacheVariables": { 29 | "CMAKE_BUILD_TYPE": "Debug" 30 | } 31 | }, 32 | { 33 | "name": "x64-release", 34 | "displayName": "x64 Release", 35 | "inherits": "x64-debug", 36 | "cacheVariables": { 37 | "CMAKE_BUILD_TYPE": "Release" 38 | } 39 | }, 40 | { 41 | "name": "linux-default", 42 | "displayName": "Linux Debug", 43 | "description": "Sets Ninja generator, compilers, build and install directory, debug build type", 44 | "generator": "Ninja", 45 | "binaryDir": "${sourceDir}/out/build/${presetName}", 46 | "cacheVariables": { 47 | "CMAKE_BUILD_TYPE": "Debug", 48 | "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}" 49 | }, 50 | "vendor": { 51 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 52 | "hostOS": [ "Linux" ] 53 | }, 54 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 55 | "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" 56 | } 57 | } 58 | }, 59 | { 60 | "name": "x64_debug_clang", 61 | "displayName": "Clang Debug", 62 | "description": "Target Windows (64-bit) with the Visual Studio development environment. (Debug)", 63 | "inherits": "windows-base", 64 | "architecture": { 65 | "value": "x64", 66 | "strategy": "external" 67 | }, 68 | "cacheVariables": { 69 | "CMAKE_BUILD_TYPE": "Debug", 70 | "CMAKE_C_COMPILER": "clang.exe", 71 | "CMAKE_CXX_COMPILER": "clang.exe" 72 | } 73 | } 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (C) 2025 Jason Roelofs 2 | Paul Brannan , 3 | Charlie Savage 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions 7 | are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /doc/api.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | C++ API 3 | ======== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | api/overview -------------------------------------------------------------------------------- /doc/api/overview.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | Ruby C++ API 4 | ============ 5 | 6 | Rice provides builtin wrappers for many builtin Ruby types, including: 7 | 8 | - Object 9 | - Module 10 | - Class 11 | - String 12 | - Array 13 | - Hash 14 | - Struct 15 | - Symbol 16 | - Exception 17 | 18 | Rice mimics the Ruby class hierarchy as closely as it can. 19 | 20 | For example: 21 | 22 | .. code-block:: cpp 23 | 24 | Object object_id = obj.call("object_id"); 25 | std::cout << object_id << std::endl; 26 | 27 | Class rb_cTest = define_class("Test"); 28 | Object object_id = rb_cTest.call("object_id"); 29 | std::cout << object_id << std::endl; 30 | 31 | The ``Array`` and ``Hash`` types can even be iterated over the same way one 32 | would iterate over an STL container: 33 | 34 | .. code-block:: cpp 35 | 36 | Array a; 37 | a.push(detail::To_Ruby().convert(42)); 38 | a.push(detail::To_Ruby().convert(43)); 39 | a.push(detail::To_Ruby().convert(44)); 40 | Array::iterator it = a.begin(); 41 | Array::iterator end = a.end(); 42 | for(; it != end; ++it) 43 | { 44 | std::cout << *it << std::endl; 45 | } 46 | 47 | STL algorithms should also work as expected on ``Array`` and ``Hash`` containers. 48 | 49 | -------------------------------------------------------------------------------- /doc/architecture.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Architecture 3 | ============ 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | architecture/enumerators -------------------------------------------------------------------------------- /doc/bindings.rst: -------------------------------------------------------------------------------- 1 | ======== 2 | Bindings 3 | ======== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | bindings/constructors 9 | bindings/methods 10 | bindings/overloaded_methods 11 | bindings/attributes 12 | bindings/enums 13 | bindings/constants 14 | bindings/iterators 15 | bindings/buffers 16 | bindings/class_templates 17 | bindings/operators 18 | bindings/exceptions 19 | bindings/types_overview 20 | bindings/type_conversions 21 | bindings/type_verification 22 | bindings/memory_management 23 | bindings/inheritance 24 | bindings/callbacks 25 | bindings/instance_registry 26 | 27 | -------------------------------------------------------------------------------- /doc/bindings/attributes.rst: -------------------------------------------------------------------------------- 1 | .. _attributes: 2 | 3 | Attributes 4 | ========== 5 | C++ structures, and sometimes classes, often have public member variables that store data. Rice makes it easy to wrap these member variables via the use of ``define_attr``: 6 | 7 | .. code-block:: cpp 8 | 9 | struct MyStruct 10 | { 11 | int readOnly = 0; 12 | int writeOnly = 0; 13 | int readWrite = 0; 14 | }; 15 | 16 | Data_Type rb_cMyStrut = 17 | define_class("MyStruct") 18 | .define_constructor(Constructor()) 19 | .define_attr("read_only", &MyStruct::readOnly, Rice::AttrAccess::Read) 20 | .define_attr("write_only", &MyStruct::writeOnly, Rice::AttrAccess::Write) 21 | .define_attr("read_write", &MyStruct::readWrite); 22 | } 23 | 24 | Notice the use of ``Rice::AttrAccess::Read`` to define read-only attributes and ``Rice::AttrAccess::Write`` for write-only attributes. If you do not specify an AttrAccess value then Rice make the attribute readable and writable. 25 | 26 | These attributes can then be accessed in the expected way in Ruby: 27 | 28 | .. code-block:: ruby 29 | 30 | my_struct = MyStruct.new 31 | a = my_struct.read_only 32 | my_struct.write_only = 5 33 | my_struct.read_write = 10 34 | b = my_struct.read_write 35 | 36 | Similarly, you can wrap static members via the use of ``define_singleton_attr``: 37 | 38 | .. code-block:: cpp 39 | 40 | struct MyStruct 41 | { 42 | static int readOnly = 0; 43 | static int writeOnly = 0; 44 | static int readWrite = 0; 45 | }; 46 | 47 | Data_Type rb_cMyStrut = 48 | define_class("MyStruct") 49 | .define_constructor(Constructor()) 50 | .define_singleton_attr("read_only", &MyStruct::readOnly, Rice::AttrAccess::Read) 51 | .define_singleton_attr("write_only", &MyStruct::writeOnly, Rice::AttrAccess::Write) 52 | .define_singleton_attr("read_write", &MyStruct::readWrite); 53 | } 54 | 55 | These attributes can then be accessed in the expected way in Ruby: 56 | 57 | .. code-block:: ruby 58 | 59 | a = MyStruct.read_only 60 | MyStruct.write_only = 5 61 | MyStruct.read_write = 10 62 | b = MyStruct.read_write -------------------------------------------------------------------------------- /doc/bindings/constants.rst: -------------------------------------------------------------------------------- 1 | .. _constants: 2 | 3 | Constants 4 | ========= 5 | C++ allows constants to be defined globally, on namespaces and classes/structs. 6 | 7 | .. code-block:: cpp 8 | 9 | constexpr int CONSTANT_1 = 1; 10 | 11 | namespace SomeNamespace 12 | { 13 | constexpr int CONSTANT_2 = 2; 14 | } 15 | 16 | class MyClass 17 | { 18 | static constexpr int CONSTANT_3 = 3; 19 | } 20 | 21 | These constants can be wrapped like this: 22 | 23 | .. code-block:: cpp 24 | 25 | Object(rb_cObject).define_constant("CONSTANT_1", CONSTANT_1); 26 | 27 | Module rb_mSomeNamespace = define_module("SomeNamespace"); 28 | rb_mSomeNamespace.define_constant("CONSTANT_2", CONSTANT_2); 29 | 30 | Data_Type rb_cMyClass = define_class("MyClass"). 31 | define_constant("CONSTANT_3", CONSTANT_3); 32 | 33 | Enums as Constants 34 | ------------------ 35 | Older C++ code sometimes uses anonymous C style enums as a hack for defining class constants. For more information see :ref:`anonymous_enums`. 36 | -------------------------------------------------------------------------------- /doc/bindings/instance_registry.rst: -------------------------------------------------------------------------------- 1 | .. _Instance Registry: 2 | 3 | Instance Registry 4 | ================= 5 | 6 | Rice 4.1 added an instance registry which tracks which C++ objects have been wrapped by Ruby objects. This done via a global ``std::map`` maintained by Rice. 7 | 8 | Enabled 9 | ------- 10 | When the instance registry is enabled, Rice will check if a C++ instance has been wrapped by a Ruby instance. If it has, then the existing Ruby instance is returned. 11 | 12 | By default, instance tracking is disabled. To turn it on: 13 | 14 | .. code-block:: cpp 15 | 16 | detail::Internal::intance.instances.isEnabled = true; 17 | 18 | Disabled 19 | -------- 20 | When the instance registry is disabled, Rice will wrap a C++ instance in a new Ruby instance regardless of whether it is already wrapped by a Ruby instance. Therefore if you make multiple calls to a C++ method that returns the same C++ object each time via a reference or pointer, multiple wrapping Ruby objects will be created. By default having multiple Ruby objects wrap a C++ object is fine since the Ruby objects do not own the C++ object. For more information please carefully read the :ref:`cpp_to_ruby` topic. 21 | 22 | There is one exception to this rule, which happens when a C++ method returns back itself. Rice recognizes that the C++ object is wrapped by the Ruby object making the call, and thus it is returning self (see :ref:`return_self`). 23 | 24 | Why is Tracking Disabled? 25 | ------------------------- 26 | Enabling the instance registry can significantly increase performance. Although tracking introduces a small amount of overhead, it avoids creating duplicate Ruby objects and C++ wrapper objects. 27 | 28 | However, its unknown if tracking is fully reliable. There are a few potential issues. 29 | 30 | First, the implementation is not thread-safe. Due to Ruby's GIL, this is not considered an issue. 31 | 32 | Second, pairs in the global map are removed when a Ruby object is freed by the garbage collector. There could be a window where a Ruby object is marked for deletion but the underlying C++ object is returned back to Ruby. Then the Ruby object would be freed resulting in a crash. It is unknown if this really happens, it has never been observed. 33 | 34 | Third, a C++ instance wrapped by Ruby instance may be freed on the C++ side. As long as ownership rules have been correctly setup, this is fine. However, if a new C++ instance is created that has the same address as the deleted C++ object and then is passed to Ruby the instance tracker will return the old deleted object. This has been observed in the Rice tests. It is unknown if this is due to how the tests are written or is a more general problem. -------------------------------------------------------------------------------- /doc/bindings/type_verification.rst: -------------------------------------------------------------------------------- 1 | .. _type_verification: 2 | 3 | Type Verification 4 | ================== 5 | 6 | Rice is very picky about types - it will throw an exception on startup if it comes across a type that has not been defined. Hopefully this will not come as a surprise to most C++ developers. 7 | 8 | For example, let's look at the following contrived example: 9 | 10 | .. code-block:: cpp 11 | 12 | class MyClass 13 | { 14 | }; 15 | 16 | using Fancy_T = std::vector>> 17 | 18 | class Factory 19 | { 20 | Fancy_T& make_fancy_type(); 21 | }; 22 | 23 | void setupRice() 24 | { 25 | define_class("Factory"). 26 | define_method("make_fancy_type", &Factory::make_fancy_type); 27 | } 28 | 29 | When Ruby loads the Rice extension above it will throw an exception. The reason is that ``MyClass`` has not yet been defined to Rice. 30 | 31 | When ``define_method`` is called, Rice will see that the return type for ``&Factory::make_fancy_type`` is ``Fancy_T``. It will then drill down through the vector to the unique_ptr to the pair to ``MyClass``. When it encounters ``MyClass`` it will check its internal ``TypeRegistry`` and realize it has not yet been defined and throw a ``std::runtime_exception``. 32 | 33 | To fix this requires first defining ``MyClass`` like this: 34 | 35 | .. code-block:: cpp 36 | 37 | void setupRice() 38 | { 39 | define_class("MyClass"); 40 | 41 | define_class("Factory"). 42 | define_method("make_fancy_type", &Factory::make_fancy_type); 43 | } 44 | -------------------------------------------------------------------------------- /doc/bindings/types_overview.rst: -------------------------------------------------------------------------------- 1 | .. _types_overview: 2 | 3 | Types Overview 4 | ================ 5 | 6 | The purpose of Rice is to enable native C++ code and Ruby code work together. This requires making it easy to translate types between the two languages. 7 | 8 | There are three main use cases: 9 | 10 | 1. Converting, i.e copying, types between the two languages (:doc:`Type conversion `) 11 | 2. Enable Ruby to access C++ code via a Ruby wrapper (define_class, define_enum, etc) 12 | 3. Enable C++ to access Ruby code via a C++ wrapper (:ref:`api`) 13 | 14 | :doc:`Type conversion ` works well for primitive types such as boolean and numeric types. For example, a C++ unsigned 32 bit integer is copied into a Ruby Fixnum instance (and vice versa). Conversion of primitive types is also easy to understand because its familiar to programmers. When you pass a boolean or integer into a method, you don't expect the method is going to change it - instead it just gets a copy. 15 | 16 | However, type conversion usually does not make sense for more complex types. You likely do not want to copy instances of simple C++ structures to Ruby, and you almost never want to copy instances of C++ classes. There are a lot of reasons for this, including: 17 | 18 | * C++ objects may contain uncopyable internal state, such as a database connection or an open file handle 19 | * C++ has complex object lifetime rules that control how objects are created, copied and destructed that do not translate to Ruby 20 | * A C++ object may use a lot of memory, such as a million element vector, that makes it untenable to copy it to Ruby. 21 | * Copying data, by definition, creates two separate versions making it impossible to share data between the two languages. 22 | 23 | As a result, a more practical approach is to provide thin wrappers that allow Ruby to access C++ objects and C++ to access Ruby objects. Ruby wrappers are created via define_enum, define_class, etc. as described in other parts of the documentation. 24 | 25 | Last, you may wish to manipulate Ruby objects from C++ using Rice's object-oriented :ref:`api` versus Ruby's C api. -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'Rice' 21 | copyright = '2024, Paul Brannon, Jason Roelofs, Charlie Savage' 22 | author = 'Paul Brannon, Jason Roelofs, Charlie Savage' 23 | 24 | 25 | # -- General configuration --------------------------------------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | ] 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ['_templates'] 35 | 36 | # List of patterns, relative to source directory, that match files and 37 | # directories to ignore when looking for source files. 38 | # This pattern also affects html_static_path and html_extra_path. 39 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 40 | 41 | 42 | # -- Options for HTML output ------------------------------------------------- 43 | 44 | # The theme to use for HTML and HTML Help pages. See the documentation for 45 | # a list of builtin themes. 46 | # 47 | html_theme = 'furo' 48 | 49 | # Add any paths that contain custom static files (such as style sheets) here, 50 | # relative to this directory. They are copied after the builtin static files, 51 | # so a file named "default.css" will overwrite the builtin "default.css". 52 | #html_static_path = ['_static'] 53 | 54 | # Due to UTF8 charactes in stl.rst in Ruby code 55 | suppress_warnings = ['misc.highlighting_failure'] 56 | -------------------------------------------------------------------------------- /doc/history.rst: -------------------------------------------------------------------------------- 1 | .. _history: 2 | 3 | History 4 | ======= 5 | Rice originated as Excruby, a project to interface with C++-based trading software at Automated Trading Desk in Mount Pleasant, South Carolina. The Ruby bindings for Swig were at the time less mature than they are today, and did not suit the needs of the project. 6 | 7 | Excruby was written as a set of helper functions and classes for interfacing with the Ruby interpreter in an exception-safe manner. Over the course of five years, the project grew into wrappers for pieces of the Ruby API, but the original helper functions remained as part of the public interface. 8 | 9 | This created confusion for the users of the library,because there were multiple ways of accomplishing most tasks -- directly through the C API, through a low-level wrapper around the C API and through a high-level abstraction of the lower-level interfaces. 10 | 11 | Rice was then born in 2008 as an attempt to clean up the interface. Rice keeps the lower-level wrappers, but as an implementation detail; the public interface is truly a high-level abstraction around the Ruby C API. 12 | 13 | However, it was still difficult to use Rice to wrap C++ libraries. A major drawback was that Rice required its own compilation step to create is own library. At the time (and still today), there is not a standard way to install compiled C++ packages. 14 | 15 | To address this, version 4, released in 2021, was a major rewrite that changed Rice into a header-only library. This made it much easier to use Rice to wrap C++ libraries. In addition, version 4 took full advantage of all the C++ template metaprogramming functionality in C++17 and higher to make it easier to create Ruby extensions for C++ libraries. 16 | 17 | In 2025, Rice version 4.5 was released. Version 4.5 was based on learnings from wrapping the OpenCV library. OpenCV exposes a large C++ API that makes heavy use of C++ templates, overridden methods and constructors, C style callbacks and other C++ features. To successfully wrap the library required making numerous changes to Rice which are described in the `CHANGELOG.md `_. 18 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Rice - Ruby Interface for C++ 3 | ============================= 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | introduction 9 | why_rice 10 | tutorial 11 | bindings 12 | stl 13 | api 14 | packaging 15 | migration 16 | history 17 | architecture -------------------------------------------------------------------------------- /doc/introduction.rst: -------------------------------------------------------------------------------- 1 | .. _introduction: 2 | 3 | ============ 4 | Introduction 5 | ============ 6 | 7 | Rice is a C++ 17 header-only library that serves dual purposes. First, it makes it much easier to create Ruby bindings for existing C++ libraries. Second, it provides an object oriented interface to Ruby's C API that makes it easy to embed Ruby and write Ruby extensions in C++. 8 | 9 | Rice is similar to `Boost.Python `_ and `pybind11 `_ in that it minimizes boilerplate code needed to interface with C++. It does this by automatically determining type information allowing Ruby objects to be converted to C++ and vice versa. 10 | 11 | Rice provides: 12 | 13 | * A simple C++-based syntax for wrapping and defining classes 14 | * Automatic type conversions between C++ and Ruby 15 | * Automatic exception handling between C++ and Ruby 16 | * Smart pointers for handling garbage collection 17 | * Support for most builtin types 18 | 19 | Version Differences 3.x vs 4.x and later 20 | ---------------------------------------- 21 | 22 | This documentation and the ``master`` branch are for Rice 4.x and later, which is the header-only version of the library. Use the ``3.x`` branch for the docs and code for that line of releases. 23 | 24 | To upgrade a library from Rice 3 to 4, see the :ref:`migration` guide. 25 | 26 | The documentation for the 3.x line of Rice is viewable at https://ruby-rice.github.io/3.x. 27 | 28 | Project Details 29 | --------------- 30 | 31 | The source is hosted on GitHub: http://github.com/ruby-rice/rice 32 | 33 | Bug tracking is also hosted on github: http://github.com/ruby-rice/rice/issues 34 | 35 | API documentation: http://ruby-rice.github.io/4.x 36 | 37 | .. _installation: 38 | 39 | Installation 40 | ------------ 41 | 42 | .. code-block:: shell 43 | 44 | gem install rice 45 | 46 | Rice is header-only library and therefore does not need to be built separately. Instead it should be #included in your C++ project. Rice requires C++17 or later and is tested on Windows (MSVC and Mingw64), MacOS (Xcode/clang) and Linux (g++). 47 | 48 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /doc/packaging.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Packaging 3 | ========= 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | packaging/packaging 9 | packaging/extconf.rb 10 | packaging/cmake -------------------------------------------------------------------------------- /doc/packaging/extconf.rb.rst: -------------------------------------------------------------------------------- 1 | .. _extconf.rb: 2 | 3 | extconf.rb 4 | ========== 5 | The default way to build ``RubyGem`` extensions is to use ``extconf.rb``. However, ``extconf.rb`` was designed for ``C`` and not `C++`. Thus, Rice provides a file called ``mkmf-rice`` that adds additional functionality to ``mkmf`` including: 6 | 7 | * Uses a C++ compiler instead of a C compiler 8 | * Includes Rice header files automatically 9 | * Sets required :ref:`compiler_settings` 10 | * Links to the standard C++ library 11 | 12 | Enabling 13 | -------- 14 | To build your extension using ``extconf.rb``, add the following to your Gemspec (assuming the extension directory is ``ext``): 15 | 16 | .. code-block:: ruby 17 | 18 | Gem::Specification.new do |spec| 19 | spec.extensions = ["ext/extconf.rb"] 20 | end 21 | 22 | Example 23 | ------- 24 | Below is an example ``extconf.rb``: 25 | 26 | .. code-block:: ruby 27 | 28 | require 'mkmf-rice' 29 | 30 | dir_config('...') 31 | have_library('...') 32 | create_makefile('my_extension') 33 | 34 | Disadvantages 35 | ------------- 36 | Although ``extconf.rb`` is widely used to build Ruby extensions, it is not designed for C++. This means it can be tricky to get it to work for C++ extensions. 37 | 38 | Aother disadvantage is that ``extconf.rb`` creates a ``Makefile`` that is then built either by ``make`` or ``nmake``. By default, ``make`` will only compile a single file at a time. For larger C++ extensions, such as ``ruby-opencv`` this results is very *long* compilation times. -------------------------------------------------------------------------------- /doc/stl.rst: -------------------------------------------------------------------------------- 1 | === 2 | STL 3 | === 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | stl/stl 9 | stl/complex 10 | stl/exception 11 | stl/exception_ptr 12 | stl/map 13 | stl/multimap 14 | stl/optional 15 | stl/pair 16 | stl/reference_wrapper 17 | stl/set 18 | stl/smart_pointers 19 | stl/string 20 | stl/string_view 21 | stl/tuple 22 | stl/type_index 23 | stl/type_info 24 | stl/unordered_map 25 | stl/variant 26 | stl/vector 27 | -------------------------------------------------------------------------------- /doc/stl/complex.rst: -------------------------------------------------------------------------------- 1 | .. _std_complex: 2 | 3 | std::complex 4 | ============ 5 | Similarly to ``std::string``, Rice considers ``std::complex`` a Builtin type. That means that ``std::complex`` values are converted to Ruby ``Complex`` instances, and vice versa. 6 | -------------------------------------------------------------------------------- /doc/stl/exception.rst: -------------------------------------------------------------------------------- 1 | .. _std_exception: 2 | 3 | std::exception 4 | ============== 5 | Most of the time you do not have to worry about C++ exceptions. Rice :ref:`automatically ` handles C++ exceptions and converts them to the appropriate Ruby exception. 6 | 7 | However, although rare, some C++ APIs define methods that take exception parameters. For example, OpenCV's API includes: 8 | 9 | .. code-block:: cpp 10 | 11 | class AsyncPromise 12 | { 13 | public: 14 | /** Stores exception. 15 | @param[in] exception exception to be raised in AsyncArray */ 16 | void setException(const cv::Exception& exception); 17 | }; 18 | 19 | Thus Rice provides access to creating new C++ exception instances: 20 | 21 | .. code-block:: cpp 22 | 23 | exception = Std::Exception.new 24 | 25 | Rice currently only supports creating instances of ``std::exception`` and not any of its subtypes. 26 | 27 | Ruby API 28 | ^^^^^^^^ 29 | The Ruby API for ``std::exception`` is: 30 | 31 | * Exception#new (see `constructor `_) 32 | * Exception#what (see `what `_) 33 | -------------------------------------------------------------------------------- /doc/stl/exception_ptr.rst: -------------------------------------------------------------------------------- 1 | .. _std_exception_ptr: 2 | 3 | std::exception_ptr 4 | ================== 5 | Introduced in C++11, ``std::exception_ptr`` enables C++ exceptions to be captured. They can then be moved between threads, handled in different parts of code base or rethrown. Generally you will not need to use them in Ruby, but some C++ APIs expose them. 6 | 7 | For example, OpenCV's API includes: 8 | 9 | .. code-block:: cpp 10 | 11 | class AsyncPromise 12 | { 13 | public: 14 | /** Stores exception. 15 | @param[in] exception exception to be raised in AsyncArray */ 16 | void setException(std::exception_ptr exception); 17 | }; 18 | 19 | Ruby API 20 | ^^^^^^^^ 21 | Currently Rice simply wraps ``std::exception_ptr`` so that it can be passed between Ruby and C++. However, Rice does not expose any constructors or methods. If this functionality is needed it can be added in the future. -------------------------------------------------------------------------------- /doc/stl/map.rst: -------------------------------------------------------------------------------- 1 | .. _std_map: 2 | 3 | std::map 4 | ======== 5 | Outside of ``std::vector``, ``std::map`` is perhaps the most commonly used container in C+. Although there is a direct conceptual mapping between a ``std::map`` and a Ruby ``Hash``, Rice does not copy a map to an Hash. Instead, it wraps ``std::map``. 6 | 7 | There are multiple reasons for this: 8 | 9 | * ``std::map`` instantiations can only contain one type of key and value, while Ruby Hashes can contain different types of keys and values 10 | * ``std::map`` instances can be quite large 11 | * ``std::map`` instances commonly contain C++ classes that have complex copy or move semantics 12 | * having two disconnected copies of data, one in C++ and one in Ruby, is usually undesirable 13 | 14 | Rice will automatically define Ruby classes for each instantiation of ``std::map`` it finds. You may also manually define Ruby classes via the use of the ``define_map`` method. Map classes are added to the ``Std`` module. 15 | 16 | Usage 17 | ^^^^^ 18 | For C++ methods that take map arguments, you can instantiate a new map from Ruby (see :ref:`stl_class_names`). 19 | 20 | For example, assume this C++ code: 21 | 22 | .. code-block:: cpp 23 | 24 | void passMap(std::map map) 25 | { 26 | } 27 | 28 | define_global_function("pass_map", &passMap); 29 | 30 | One way to call it from Ruby is like this: 31 | 32 | .. code-block:: ruby 33 | 34 | map = Std::Map≺string‚ int≻.new 35 | map["thirty seven"] = 37 36 | pass_map(map) 37 | 38 | In this case, Ruby is wrapping a C++ map. Therefore any changes made to the map in C++ will be visible to Ruby. 39 | 40 | A second way to call the method is to pass a Ruby Hash: 41 | 42 | .. code-block:: ruby 43 | 44 | hash = {"three" => 3, "five" => 5, "nine" => 9} 45 | pass_map(hash) 46 | 47 | In this case, Rice will *copy* the Ruby hash instead of wrapping it. Thus any modifications made in C++ will not be visible to Ruby. 48 | 49 | Ruby API 50 | ^^^^^^^^ 51 | Rice tries to make ``std::map`` look like a Ruby Hash by giving it an API that is a subset of ``Hash``. However, there are differences you need to keep in mind. 52 | 53 | First, the following methods only work if the map type is copyable (copying is done in C++): 54 | 55 | * Map#copy(other) 56 | 57 | Second, the following methods only work if the map type implements C++'s equal operator, ``operator==``: 58 | 59 | * Map#value? 60 | 61 | Third, if the map type supports C++ streams, then the following method will work, otherwise it will return "Not Printable" 62 | 63 | * Map#to_s 64 | 65 | -------------------------------------------------------------------------------- /doc/stl/multimap.rst: -------------------------------------------------------------------------------- 1 | .. _std_multimap: 2 | 3 | std::multimap 4 | ============= 5 | Ruby does not natively provide MultiMaps. Thus Rice wraps ``std::multimap``. 6 | 7 | Rice will automatically define Ruby classes for each instantiation of ``std::multimap`` it finds. You may also manually define Ruby classes via the use of the ``define_multimap`` method. Multimap classes are added to the ``Std`` module. 8 | 9 | Usage 10 | ^^^^^ 11 | For C++ methods that take multimap arguments, you can instantiate a new multimap from Ruby (see :ref:`stl_class_names`). 12 | 13 | For example, assume this C++ code: 14 | 15 | .. code-block:: cpp 16 | 17 | void passMultiMap(std::multimap stringMultimap) 18 | { 19 | } 20 | 21 | define_global_function("pass_multimap", &passMultiMap); 22 | 23 | One way to call it from Ruby is like this: 24 | 25 | .. code-block:: ruby 26 | 27 | multimap = Std::Map≺string‚ int≻.new 28 | multimap["thrity seven"] = "thirty seven" 29 | pass_multimap(multimap) 30 | 31 | In this case, Ruby is wrapping a C++ multimap. Therefore any changes made to the multimap in C++ will be visible to Ruby. 32 | 33 | A second alternative is to pass it a Ruby hash instead: 34 | 35 | .. code-block:: ruby 36 | 37 | hash = {"three" => 3, "five" => 5, "nine" => 9} 38 | pass_multimap(hash) 39 | 40 | In this case, Rice will *copy* the Ruby hash instead of wrapping it. Thus any modifications made in C++ will not be visible to Ruby. 41 | 42 | Ruby API 43 | ^^^^^^^^ 44 | Rice tries to make ``std::multimap`` look like a Ruby Hash by giving it an API that is a subset of ``Hash``. However, there are differences you need to keep in mind. 45 | 46 | First, the following methods only work if the multimap type is copyable (copying is done in C++): 47 | 48 | * multimap#copy(other) 49 | 50 | Second, the following methods only work if the multimap type implements C++'s equal operator, ``operator==``: 51 | 52 | * multimap#value? 53 | 54 | Third, if the multimap type supports C++ streams, then the following method will work, otherwise it will return "Not Printable" 55 | 56 | * multimap#to_s 57 | 58 | -------------------------------------------------------------------------------- /doc/stl/optional.rst: -------------------------------------------------------------------------------- 1 | .. _std_optional: 2 | 3 | std::optional 4 | ============= 5 | Introduced in C++17, ``std::optional`` provides C++ code an additional way (besides pointers) to return values from functions that may not be set. 6 | 7 | Since Ruby does not have an equivalent type, Rice unwraps ``std::optional`` instances. If the optional is empty, thus has a value of ``std::nullopt``, Rice converts it to ``nil`` in Ruby. If the optional has a value, and that value is a Builtin type, then it will be converted to the appropriate Ruby type. If it is not a Builtin type, then the value will be wrapped by Ruby. 8 | 9 | When passing a Ruby instance to ``std::optional``, Rice will convert ``nil`` values to ``std::nullopt`` and non ``nil`` values to the appropriate C++ type. -------------------------------------------------------------------------------- /doc/stl/pair.rst: -------------------------------------------------------------------------------- 1 | .. _std_pair: 2 | 3 | std::pair 4 | ========= 5 | ``std::pair`` is a simple container that provides C++ code a way of associating two values. ``std::map`` and ``std::unordered_map`` use ``std::pair`` to hold keys and their associated values. 6 | 7 | Ruby does not have a concept of a pair. Therefore, Rice wraps ``std::pair`` which means that data is not copied between C++ and Ruby. 8 | 9 | Since ``std::pair`` is a template of two types, each ``std::pair`` instantiation is its own unique C++ class, and thus its own unique Ruby class. You may manually define pair classes or let Rice do it for you. To manually define a Ruby class, use either the ``define_pair`` method. 10 | 11 | Example: 12 | 13 | .. code-block:: cpp 14 | 15 | std::pair makeStringIntPair(std::string key, uint32_t value) 16 | { 17 | return std::make_pair(key, value); 18 | } 19 | 20 | define_pair("StringIntPair"); 21 | define_global_function("make_string_int_pair", &makeStringIntPair); 22 | 23 | Once you have defined this Ruby class, you can create a new instance like this: 24 | 25 | .. code-block:: cpp 26 | 27 | pair = StringIntPair.new("key 2", 33) 28 | 29 | 30 | Ruby API 31 | ^^^^^^^^ 32 | The Ruby API exposed for ``std::pair`` should be fairly self-explanatory and consists of the following methods (assume we have created a Ruby class called Pair): 33 | 34 | * Pair#new(value1, value2) 35 | * Pair#first 36 | * Pair#first=(value) 37 | * Pair#second 38 | * Pair#second=(value) 39 | 40 | If the underlying ``std::pair`` has copyable types (remember copying is done in C++), then the following method will work, otherwise it will raise an exception: 41 | 42 | * PairClass#copy(other) 43 | 44 | If the underlying ``std::pair`` has types that are supported by C++ streams, then the following method will work, otherwise it will return "Not Printable" 45 | 46 | * PairClass#to_s 47 | 48 | -------------------------------------------------------------------------------- /doc/stl/reference_wrapper.rst: -------------------------------------------------------------------------------- 1 | .. _std_reference_wrapper: 2 | 3 | std::reference_wrapper 4 | ====================== 5 | Introduced in C++11, ``std::reference_wrapper`` wraps C++ references in a copyable, assignable object. This allows them to be stored in containers such as ``std::vector`` or other types such as ``std::variant``. 6 | 7 | Since Ruby does not have an equivalent type, Rice unwraps ``std::reference_wrapper`` instances. If the ``std::reference_wrapper`` points to a Builtin type, then it will be converted to the appropriate Ruby type. It it points to a non Builtin type, then the value will be wrapped by Ruby. 8 | 9 | When passing a Ruby instance to ``std::reference_wrapper``, Rice will convert a Ruby type into the appropriate C++ type and store it inside the wrapper. Note this can be dangerous because Rice must maintain the original memory location for the reference to remain valid. For wrapped types this means the reference will remain valid as long as the wrapping Ruby object is valid and not garbage collected. For BuiltIn types, Rice only guarantees the reference to be valid through the lifetime of a method call. Thus if called C++ code stores the reference, and later tries to use it, an exception will happen. 10 | -------------------------------------------------------------------------------- /doc/stl/set.rst: -------------------------------------------------------------------------------- 1 | .. _std_set: 2 | 3 | std::set 4 | =========== 5 | Although there is a direct conceptual mapping between a ``std::set`` and a Ruby ``Set``, Rice does not copy a C++ set to an Ruby set. Instead, it wraps ``std::set``. 6 | 7 | There are multiple reasons for this: 8 | 9 | * ``std::set`` instantiations can only contain one type, while Ruby Sets can contain different types 10 | * ``std::set`` instances can be quite large 11 | * ``std::set`` instances commonly contain C++ classes that have complex copy or move semantics 12 | * having two disconnected copies of data, one in C++ and one in Ruby, is usually undesirable 13 | 14 | Rice will automatically define Ruby classes for each instantiation of ``std::set`` it finds. You may also manually define Ruby classes via the use of the ``define_set`` method. Set classes are added to the ``Std`` module. 15 | 16 | Usage 17 | ^^^^^ 18 | For C++ methods that take set arguments, you can instantiate a new set from Ruby (see :ref:`stl_class_names`). 19 | 20 | For example, assume this C++ code: 21 | 22 | .. code-block:: cpp 23 | 24 | void passSet(std::set ints) 25 | { 26 | } 27 | 28 | define_global_function("pass_set", &passSet); 29 | 30 | One way to call it from Ruby is like this: 31 | 32 | .. code-block:: ruby 33 | 34 | set = Std::Set≺int≻.new 35 | set.push(37) 36 | pass_set(set) 37 | 38 | In this case, Ruby is wrapping a C++ set. Therefore any changes made to the set in C++ will be visible to Ruby. 39 | 40 | A second way is to pass a Ruby Set like this: 41 | 42 | .. code-block:: ruby 43 | 44 | set = Set.new([3, 5, 9]) 45 | pass_set(set) 46 | 47 | In this case, Rice will *copy* the Ruby array instead of wrapping it. Thus any modifications made in C++ will not be visible to Ruby. 48 | 49 | Ruby API 50 | ^^^^^^^^ 51 | Rice tries to make a ``std::set`` look like a Ruby Set by defining an API that is a subset of ``Set``. However, there are differences you need to keep in mind. 52 | 53 | If the set type supports C++ streams, then the following method will work, otherwise it will return "Not Printable" 54 | 55 | * set#to_s 56 | -------------------------------------------------------------------------------- /doc/stl/string.rst: -------------------------------------------------------------------------------- 1 | .. _std_string: 2 | 3 | std::string 4 | ============ 5 | Perhaps ``std::string`` is the most commonly used class in the C++ standard template library. Rice treats ``std::string``'s as Builtin types, which means strings are copied between C++ and Ruby. Thus, if you pass a ``std::string`` to Ruby and modify it in Ruby, C++ will not see the changes (and vice versa). 6 | 7 | Unlike Ruby, C++ has very little support for encodings. Thus it is a guessing game to correctly translate strings between C++ and Ruby and its up to you to get it right. 8 | 9 | When Rice converts a ``std::string`` to Ruby it is assumed to have the encoding specified by ``Encoding.default_external``. That is likely to be UTF-8 on Windows while on Linux and MacOS it is based on the operating system locale. If no external encoding is specified, the converted string will have an encoding ASCII-8BIT which is Ruby's way of saying it has no encoding at all. If the encoding is incorrect, then you need to fix it in Ruby by calling ``String#force_encoding``. 10 | 11 | In contrast, when Rice converts a Ruby string to a ``std::string`` it simply passes the underlying ``char`` buffer to ``std::string`` for copying. Thus it is once again up to you to make sure the encoding is correctly set in Ruby before passing the string to C++. 12 | 13 | Note that Rice does not support ``std::wstring``. 14 | -------------------------------------------------------------------------------- /doc/stl/string_view.rst: -------------------------------------------------------------------------------- 1 | .. _std_string_view: 2 | 3 | std::string_view 4 | ================ 5 | ``std::string_view`` is a read-only reference to a sequence of ``char``. It provides a way of passing strings without the overhead of copying ``std::string``. 6 | 7 | On input, Rice treats ``std::string_view`` as a Builtin type which means it copies the portion of the ``char`` sequence that a ``std::string_view`` references in C++ to Ruby. Please refer to the :ref:`std_string` documentation to learn about how Rice handles encodings. 8 | 9 | On output, Rice creates a ``std::string_view`` that references a Ruby string's underlying ``char`` buffer. Note this is DANGEROUS. Sooner or later the Ruby string will be garbage collected or moved as part of compaction, thus invalidating the ``char`` buffer. So use this with caution. 10 | -------------------------------------------------------------------------------- /doc/stl/tuple.rst: -------------------------------------------------------------------------------- 1 | .. _std_tuple: 2 | 3 | std::tuple 4 | ============ 5 | ``std::tuple`` is a fixed size container that may contain heterogeneous values. 6 | 7 | Ruby does not have the concept of a tuple, but it can be mapped to a fixed size array. Thus Rice unwraps ``std::tuple`` instances into an array that has the same size as the tuple. 8 | 9 | Rice also support passing a Ruby array to C++ api that takes tuples. 10 | 11 | .. _out_parameters_tuple: 12 | 13 | Out Parameters 14 | -------------- 15 | Prior to the introduction of tuples, C++ did not have a good way of returning multiple values from a function. One workaround was to return values via function parameters that are pointers. These are known as ``out`` parameters. 16 | 17 | For example, the `minMaxLoc `_ function in OpenCV is defined as: 18 | 19 | .. code-block:: cpp 20 | 21 | void cv::minMaxLoc(cv::InputArray src, 22 | double * minVal, 23 | double * maxVal = 0, 24 | Point * minLoc = 0, 25 | Point * maxLoc = 0, 26 | cv::InputArray mask = cv::noArray() ) 27 | 28 | All of ``minVal``, ``maxVal``, ``minLoc`` and ``maxLoc`` are out parameters designed to return values. 29 | 30 | One way to wrap this function is to use :ref:`Buffers`. 31 | 32 | An alternative approach is to use ``std::tuple``. 33 | 34 | .. code-block:: cpp 35 | 36 | define_module_function("min_max_loc", [](cv::InputArray src, cv::InputArray mask = cv::noArray()) -> std::tuple 37 | { 38 | double minVal = 0; 39 | double maxVal = 0; 40 | cv::Point minLoc = 0; 41 | cv::Point maxLoc = 0; 42 | 43 | cv::minMaxLoc(src, &minVal, &maxVal, &minLoc, &maxLoc, mask); 44 | return std::forward_as_tuple(minVal, maxVal, minLoc, maxLoc); 45 | }, 46 | Arg("src"), Arg("mask") = static_cast(cv::noArray())); 47 | 48 | Then to call the method from Ruby: 49 | 50 | .. code-block:: ruby 51 | 52 | mat = Cv::Mat.new(2, 2, CV_8UC4, cv::Scalar.new(10, 20, 30, 40)) 53 | min_val = 10 54 | min_val, max_val, min_loc, max_loc = min_max_loc(mat.input_array, min_val) 55 | 56 | -------------------------------------------------------------------------------- /doc/stl/type_index.rst: -------------------------------------------------------------------------------- 1 | .. _std_type_index: 2 | 3 | std::type_index 4 | =============== 5 | Introduced in C++11, ``std::type_index`` is a wrapper class around a ``std::type_info`` object, that can be used as index in associative and unordered associative containers. 6 | 7 | Ruby does not have a concept of a type_index. Therefore, Rice wraps ``std::type_index`` which means that data is not copied between C++ and Ruby. 8 | 9 | Ruby API 10 | ^^^^^^^^ 11 | The Ruby API for ``std::type_index`` is: 12 | 13 | * TypeIndex#hash_code (see `hash_code `_) 14 | * TypeIndex#name (see `name `_) 15 | -------------------------------------------------------------------------------- /doc/stl/type_info.rst: -------------------------------------------------------------------------------- 1 | .. _std_type_info: 2 | 3 | std::type_info 4 | ============== 5 | Instances of the class ``std:::type_info`` are returned by C++'s ``typeid`` operator and provides implementation-specific information about a type. 6 | 7 | Ruby API 8 | ^^^^^^^^ 9 | Rice wraps ``std:::type_info``. The exposed API is: 10 | 11 | * TypeInfo#hash_code (see `hash_code `_) 12 | * TypeInfo#name (see `name `_) 13 | -------------------------------------------------------------------------------- /doc/stl/unordered_map.rst: -------------------------------------------------------------------------------- 1 | .. _std_unordered_map: 2 | 3 | std::unordered_map 4 | ================== 5 | ``std::unordered_map`` is the Hash table of C++. However, Rice does not copy unordered_maps to Hash tables and instead wraps them. 6 | 7 | There are multiple reasons for this: 8 | 9 | * ``std::unordered_map`` instantiations can only contain one type of key and value, while Ruby Hashes can contain different types of keys and values 10 | * ``std::unordered_map`` instances can be quite large 11 | * ``std::unordered_map`` instances commonly contain C++ classes that have complex copy or move semantics 12 | * having two disconnected copies of data, one in C++ and one in Ruby, is usually undesirable 13 | 14 | Rice will automatically define Ruby classes for each instantiation of ``std::unordered_map`` it finds. You may also manually define Ruby classes via the use of the ``define_unordered_map`` method. Unordered Map classes are added to the ``Std`` module. 15 | 16 | Usage 17 | ^^^^^ 18 | For C++ methods that take map arguments, you can instantiate a new map from Ruby (see :ref:`stl_class_names`). 19 | 20 | For example, assume this C++ code: 21 | 22 | .. code-block:: cpp 23 | 24 | void passMap(std::unordered_map stringIntMap) 25 | { 26 | } 27 | 28 | define_unordered_map>("StringIntMap"); 29 | define_global_function("pass_map", &passMap); 30 | 31 | One way to call it from Ruby is like this: 32 | 33 | .. code-block:: ruby 34 | 35 | map = Std::UnorderedMap≺string‚ int≻.new 36 | map["thrity seven"] = 37 37 | pass_map(map) 38 | 39 | In this case, Ruby is wrapping a C++ map. Therefore any changes made to the map in C++ will be visible to Ruby. 40 | 41 | However, it is often more convenient to pass a Ruby hash instead. This is especially true if you are using Rice's :ref:`automatic ` stl classes. 42 | 43 | Therefore Rice also supports this usage: 44 | 45 | .. code-block:: ruby 46 | 47 | hash = {"three" => 3, "five" => 5, "nine" => 9} 48 | pass_map(hash) 49 | 50 | In this case, Rice will *copy* the Ruby hash instead of wrapping it. Thus any modifications made in C++ will not be visible to Ruby. 51 | 52 | Ruby API 53 | ^^^^^^^^ 54 | Rice tries to make ``std::unordered_map`` look like a Ruby Hash by giving it an API that is a subset of ``Hash``. However, there are differences you need to keep in mind. 55 | 56 | First, the following methods only work if the map type is copyable (copying is done in C++): 57 | 58 | * UnorderedMap#copy(other) 59 | 60 | Second, the following methods only work if the map type implements C++'s equal operator, ``operator==``: 61 | 62 | * UnorderedMap#value? 63 | 64 | Third, if the map type supports C++ streams, then the following method will work, otherwise it will return "Not Printable" 65 | 66 | * UnorderedMap#to_s 67 | -------------------------------------------------------------------------------- /doc/stl/variant.rst: -------------------------------------------------------------------------------- 1 | .. _std_variant: 2 | 3 | std::variant 4 | ============ 5 | Introduced in C++17, ``std::variant`` is a type safe Union. A variant can hold a single value of one of its supported types. 6 | 7 | Since a Ruby variable can point to a value of any type, Ruby does not need or have an equivalent type. Thus Rice unwraps ``std::variant`` instances and converts the stored value to the appropriate Ruby type. 8 | 9 | When passing a Ruby instance to ``std::variant``, Rice will convert a Ruby type into the appropriate C++ type and store it inside the variant. -------------------------------------------------------------------------------- /doc/stl/vector.rst: -------------------------------------------------------------------------------- 1 | .. _std_vector: 2 | 3 | std::vector 4 | =========== 5 | Along with ``std::string``, ``std::vector`` is a workhorse of many C++ code bases. Although there is a direct conceptual mapping between a ``std::vector`` and a Ruby ``Array``, Rice does not copy a vector to an Array. Instead, it wraps ``std::vector``. 6 | 7 | There are multiple reasons for this: 8 | 9 | * ``std::vector`` instantiations can only contain one type, while Ruby Arrays can contain different types 10 | * ``std::vector`` instances can be quite large 11 | * ``std::vector`` instances commonly contain C++ classes that have complex copy or move semantics 12 | * having two disconnected copies of data, one in C++ and one in Ruby, is usually undesirable 13 | 14 | Rice will automatically define Ruby classes for each instantiation of ``std::vector`` it finds. You may also manually define Ruby classes via the use of the ``define_vector`` method. Vector classes are added to the ``Std`` module. 15 | 16 | Usage 17 | ^^^^^ 18 | For C++ methods that take vector arguments, you can instantiate a new vector from Ruby (see :ref:`stl_class_names`). 19 | 20 | For example, assume this C++ code: 21 | 22 | .. code-block:: cpp 23 | 24 | void passVector(std::vector ints) 25 | { 26 | } 27 | 28 | define_vector("IntVector"); 29 | define_global_function("pass_vector", &passVector); 30 | 31 | One way to call it from Ruby is like this: 32 | 33 | .. code-block:: ruby 34 | 35 | vector = IntVector.new 36 | vector.push(37) 37 | pass_vector(vector) 38 | 39 | In this case, Ruby is wrapping a C++ vector. Therefore any changes made to the vector in C++ will be visible to Ruby. 40 | 41 | However, it is often more convenient to pass a Ruby array instead. This is especially true if you are using Rice's :ref:`automatic ` stl classes. 42 | 43 | Therefore Rice also supports this usage: 44 | 45 | .. code-block:: ruby 46 | 47 | array = [3, 5, 9] 48 | pass_vector(array) 49 | 50 | In this case, Rice will *copy* the Ruby array instead of wrapping it. Thus any modifications made in C++ will not be visible to Ruby. 51 | 52 | Ruby API 53 | ^^^^^^^^ 54 | Rice tries to make ``std::vector`` look like a Ruby Array by giving it an API that is a subset of ``Array``. However, there are differences you need to keep in mind. 55 | 56 | First, the following methods only work if the vector type is copyable (copying is done in C++): 57 | 58 | * Vector#copy(other) 59 | * Vector#resize 60 | 61 | Second, the following methods only work if the vector type implements C++'s equal operator, ``operator==``: 62 | 63 | * Vector#delete 64 | * Vector#include? 65 | * Vector#index 66 | 67 | Third, if the vector type supports C++ streams, then the following method will work, otherwise it will return "Not Printable" 68 | 69 | * Vector#to_s 70 | 71 | -------------------------------------------------------------------------------- /lib/make_rice_headers.rb: -------------------------------------------------------------------------------- 1 | require 'stringio' 2 | 3 | LINE_ENDING = "\n" 4 | RICE_INCLUDE_REGEX = %r{#include "(.*)"} 5 | OTHER_INCLUDE_REGEX = %r{#include <(.*)>} 6 | 7 | RICE_HEADER_GUARD_1 = %r{#ifndef Rice__} 8 | RICE_HEADER_GUARD_2 = %r{#define Rice__} 9 | RICE_HEADER_GUARD_3 = %r{#endif\s*//\s*Rice__} 10 | SHARED_METHODS_REGEX = %r{#include "((?:cpp_api\/)?shared_methods.hpp)"} 11 | 12 | def load_file(relative_path) 13 | content = File.read(relative_path, mode: 'rb') 14 | 15 | # Special case shared_methods.hpp which if requested we want to 16 | # merge into the current file 17 | match = content.match(SHARED_METHODS_REGEX) 18 | if match 19 | shared_path = File.join(File.dirname(relative_path), match[1]) 20 | content.gsub!(SHARED_METHODS_REGEX, File.read(shared_path, mode: 'rb')) 21 | end 22 | 23 | content 24 | end 25 | 26 | def strip_includes(content) 27 | content.lines.find_all do |line| 28 | !line.match(RICE_INCLUDE_REGEX) 29 | end.join 30 | end 31 | 32 | def add_include(path, stream) 33 | basename = File.basename(path) 34 | basename_no_ext = File.basename(path, ".*") 35 | 36 | stream << "\n" << "// ========= #{File.basename(path)} =========" << "\n" 37 | 38 | load_file(path).each_line do |line| 39 | if match = line.match(RICE_INCLUDE_REGEX) 40 | # Check for related includes, ie., Object.hpp, Object_defn.hpp and Object.ipp 41 | sub_include = File.basename(match[1]) 42 | if ["#{basename_no_ext}_defn.hpp", "#{basename_no_ext}.ipp"].include?(sub_include) 43 | sub_include_path = File.join(File.dirname(path), match[1]) 44 | stream << "\n" << "// --------- #{File.basename(sub_include_path)} ---------" << "\n" 45 | stream << strip_includes(load_file(sub_include_path)) 46 | end 47 | elsif line.match(RICE_HEADER_GUARD_1) || line.match(RICE_HEADER_GUARD_2) || line.match(RICE_HEADER_GUARD_3) 48 | # Skip the header guard 49 | else 50 | # Include the line in the output 51 | stream << line 52 | end 53 | end 54 | end 55 | 56 | def combine_headers(filename) 57 | stream = StringIO.new 58 | 59 | load_file("rice/#{filename}").each_line do |line| 60 | if matches = line.match(RICE_INCLUDE_REGEX) 61 | path = File.join("rice", matches[1]) 62 | add_include(path, stream) 63 | else 64 | stream << line 65 | end 66 | end 67 | 68 | File.open("include/rice/#{filename}", 'wb') do |file| 69 | file << stream.string 70 | end 71 | end 72 | 73 | puts "Building rice.hpp" 74 | combine_headers('rice.hpp') 75 | 76 | puts "Building stl.hpp" 77 | combine_headers('stl.hpp') 78 | 79 | puts "Success" 80 | -------------------------------------------------------------------------------- /lib/rice.rb: -------------------------------------------------------------------------------- 1 | require_relative "rice/version" 2 | -------------------------------------------------------------------------------- /lib/rice/version.rb: -------------------------------------------------------------------------------- 1 | module Rice 2 | VERSION = "4.6.0" 3 | end 4 | -------------------------------------------------------------------------------- /lib/rubygems/builder.rb: -------------------------------------------------------------------------------- 1 | class Gem::Ext::Builder 2 | alias :builder_for_original :builder_for 3 | def builder_for(extension) 4 | case extension 5 | when /CMakeLists.txt/ then 6 | Gem::Ext::CmakeBuilder.new 7 | else 8 | builder_for_original(extension) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rubygems_plugin.rb: -------------------------------------------------------------------------------- 1 | # Create RubyGems hook so we can replace the default CMakeBuilder with our own version 2 | Gem.pre_install do |installer| 3 | extensions = installer.package&.spec&.extensions 4 | if extensions && extensions.grep(/CMakeLists/) 5 | require_relative 'rubygems/builder' 6 | require_relative 'rubygems/cmake_builder' 7 | end 8 | true 9 | end -------------------------------------------------------------------------------- /rice.gemspec: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path(File.dirname(__FILE__)) 2 | require 'lib/rice/version' 3 | 4 | $spec = Gem::Specification.new do |s| 5 | s.name = 'rice' 6 | s.version = Rice::VERSION 7 | s.license = "MIT" 8 | s.summary = 'Ruby Interface for C++ Extensions' 9 | s.homepage = 'https://github.com/ruby-rice/rice' 10 | s.authors = ['Paul Brannan', 'Jason Roelofs', 'Charlie Savage'] 11 | s.email = ['curlypaul924@gmail.com', 'jasongroelofs@gmail.com', 'cfis@savagexi.com'] 12 | 13 | s.description = <<-END 14 | Rice is a C++ interface to Ruby's C API. It provides a type-safe and 15 | exception-safe interface in order to make embedding Ruby and writing 16 | Ruby extensions with C++ easier. 17 | END 18 | 19 | s.metadata = { 20 | "bug_tracker_uri" => "https://github.com/ruby-rice/rice/issues", 21 | "changelog_uri" => "https://github.com/ruby-rice/rice/blob/master/CHANGELOG.md", 22 | "documentation_uri" => "https://ruby-rice.github.io", 23 | "source_code_uri" => "https://github.com/ruby-rice/rice", 24 | } 25 | 26 | s.test_files = Dir['test/ruby/*.rb'] 27 | s.extra_rdoc_files = ['README.md'] 28 | s.require_paths = ['lib'] 29 | 30 | s.files = Dir[ 31 | # Documentation 32 | 'CHANGELOG.md', 33 | 'CONTRIBUTORS.md', 34 | 'COPYING', 35 | 'README.md', 36 | 37 | # Ruby files 38 | 'Gemfile', 39 | 'Rakefile', 40 | 'rice.gemspec', 41 | 42 | # CMake Files 43 | 'CMakeLists.txt', 44 | 'CMakePresets.json', 45 | 'FindRuby.cmake', 46 | 47 | # rice.hpp 48 | 'include/rice/rice.hpp', 49 | 'include/rice/stl.hpp', 50 | 51 | # Source files 52 | 'rice/**/*.?pp', 53 | 54 | # Ruby files 55 | 'lib/**/*.rb', 56 | 57 | # Samples 58 | 'sample/enum/extconf.rb', 59 | 'sample/enum/*.?pp', 60 | 'sample/enum/*.rb', 61 | 'sample/map/extconf.rb', 62 | 'sample/map/*.?pp', 63 | 'sample/map/*.rb', 64 | 'sample/inheritance/extconf.rb', 65 | 'sample/inheritance/*.?pp', 66 | 'sample/inheritance/*.rb', 67 | 'sample/callbacks/extconf.rb', 68 | 'sample/callbacks/*.?pp', 69 | 'sample/callbacks/*.rb', 70 | 71 | # Test source files 72 | 'test/*.?pp', 73 | 'test/extconf.rb', 74 | 'test/ext/t1/extconf.rb', 75 | 'test/ext/t1/*.*pp', 76 | 'test/ext/t2/extconf.rb', 77 | 'test/ext/t2/*.*pp' 78 | ] 79 | 80 | s.required_ruby_version = ">= 3.1" 81 | 82 | s.add_development_dependency "bundler" 83 | s.add_development_dependency "rake" 84 | s.add_development_dependency "minitest" 85 | end 86 | -------------------------------------------------------------------------------- /rice/Address_Registration_Guard.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Address_Registration_Guard__hpp_ 2 | #define Rice__Address_Registration_Guard__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A guard to register a given address with the GC. 7 | /*! Calls rb_gc_register_address upon construction and 8 | * rb_gc_unregister_address upon destruction. 9 | * For example: 10 | * \code 11 | * Class Foo 12 | * { 13 | * public: 14 | * Foo() 15 | * : string_(rb_str_new2()) 16 | * , guard_(&string_); 17 | * 18 | * private: 19 | * VALUE string_; 20 | * Address_Registration_Guard guard_; 21 | * }; 22 | * \endcode 23 | */ 24 | class Address_Registration_Guard 25 | { 26 | public: 27 | //! Register an address with the GC. 28 | /* \param address The address to register with the GC. The address 29 | * must point to a valid ruby object (RObject). 30 | */ 31 | Address_Registration_Guard(VALUE* address); 32 | 33 | //! Register an Object with the GC. 34 | /*! \param object The Object to register with the GC. The object must 35 | * not be destroyed before the Address_Registration_Guard is 36 | * destroyed. 37 | */ 38 | Address_Registration_Guard(Object* object); 39 | 40 | //! Unregister an address/Object with the GC. 41 | /*! Destruct an Address_Registration_Guard. The address registered 42 | * with the Address_Registration_Guard when it was constructed will 43 | * be unregistered from the GC. 44 | */ 45 | ~Address_Registration_Guard(); 46 | 47 | // Disable copying 48 | Address_Registration_Guard(Address_Registration_Guard const& other) = delete; 49 | Address_Registration_Guard& operator=(Address_Registration_Guard const& other) = delete; 50 | 51 | // Enable moving 52 | Address_Registration_Guard(Address_Registration_Guard&& other); 53 | Address_Registration_Guard& operator=(Address_Registration_Guard&& other); 54 | 55 | //! Get the address that is registered with the GC. 56 | VALUE* address() const; 57 | 58 | /** Called during Ruby's exit process since we should not call 59 | * rb_gc unregister_address there 60 | */ 61 | static void disable(); 62 | 63 | private: 64 | inline static bool enabled = true; 65 | inline static bool exit_handler_registered = false; 66 | static void registerExitHandler(); 67 | 68 | private: 69 | void registerAddress() const; 70 | void unregisterAddress(); 71 | 72 | VALUE* address_ = nullptr; 73 | }; 74 | } // namespace Rice 75 | 76 | #endif // Rice__Address_Registration_Guard__hpp_ -------------------------------------------------------------------------------- /rice/Address_Registration_Guard.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline Address_Registration_Guard::Address_Registration_Guard(VALUE* address) : address_(address) 4 | { 5 | registerExitHandler(); 6 | registerAddress(); 7 | } 8 | 9 | inline Address_Registration_Guard::Address_Registration_Guard(Object* object) 10 | : address_(const_cast(&object->value())) 11 | { 12 | registerExitHandler(); 13 | registerAddress(); 14 | } 15 | 16 | inline Address_Registration_Guard::~Address_Registration_Guard() 17 | { 18 | unregisterAddress(); 19 | } 20 | 21 | inline Address_Registration_Guard::Address_Registration_Guard(Address_Registration_Guard&& other) 22 | { 23 | // We don't use the constructor because we don't want to double register this address 24 | address_ = other.address_; 25 | other.address_ = nullptr; 26 | } 27 | 28 | inline Address_Registration_Guard& Address_Registration_Guard::operator=(Address_Registration_Guard&& other) 29 | { 30 | this->unregisterAddress(); 31 | 32 | this->address_ = other.address_; 33 | other.address_ = nullptr; 34 | return *this; 35 | } 36 | 37 | inline void Address_Registration_Guard::registerAddress() const 38 | { 39 | if (enabled) 40 | { 41 | detail::protect(rb_gc_register_address, address_); 42 | } 43 | } 44 | 45 | inline void Address_Registration_Guard::unregisterAddress() 46 | { 47 | if (enabled && address_) 48 | { 49 | detail::protect(rb_gc_unregister_address, address_); 50 | } 51 | 52 | address_ = nullptr; 53 | } 54 | 55 | inline VALUE* Address_Registration_Guard::address() const 56 | { 57 | return address_; 58 | } 59 | 60 | static void disable_all_guards(VALUE) 61 | { 62 | Address_Registration_Guard::disable(); 63 | } 64 | 65 | inline void Address_Registration_Guard::registerExitHandler() 66 | { 67 | if (exit_handler_registered) 68 | { 69 | return; 70 | } 71 | 72 | detail::protect(rb_set_end_proc, &disable_all_guards, Qnil); 73 | exit_handler_registered = true; 74 | } 75 | 76 | inline void Address_Registration_Guard::disable() 77 | { 78 | enabled = false; 79 | } 80 | } // Rice -------------------------------------------------------------------------------- /rice/Arg.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline Arg::Arg(std::string name) : name(name) 4 | { 5 | } 6 | 7 | template 8 | inline Arg& Arg::operator=(Arg_Type val) 9 | { 10 | this->defaultValue_ = val; 11 | return *this; 12 | } 13 | 14 | //! Check if this Arg has a default value associated with it 15 | inline bool Arg::hasDefaultValue() const 16 | { 17 | return this->defaultValue_.has_value(); 18 | } 19 | 20 | //! Return a reference to the default value associated with this Arg 21 | /*! \return the type saved to this Arg 22 | */ 23 | template 24 | inline Arg_Type Arg::defaultValue() 25 | { 26 | return std::any_cast(this->defaultValue_); 27 | } 28 | 29 | inline Arg& Arg::keepAlive() 30 | { 31 | this->isKeepAlive_ = true; 32 | return *this; 33 | } 34 | 35 | inline bool Arg::isKeepAlive() const 36 | { 37 | return this->isKeepAlive_; 38 | } 39 | 40 | inline Arg& Arg::setValue() 41 | { 42 | isValue_ = true; 43 | return *this; 44 | } 45 | 46 | inline bool Arg::isValue() const 47 | { 48 | return isValue_; 49 | } 50 | 51 | inline Arg& Arg::setOpaque() 52 | { 53 | isOpaque_ = true; 54 | return *this; 55 | } 56 | 57 | inline bool Arg::isOpaque() const 58 | { 59 | return isOpaque_; 60 | } 61 | 62 | inline Arg& Arg::takeOwnership() 63 | { 64 | this->isOwner_ = true; 65 | return *this; 66 | } 67 | 68 | inline bool Arg::isOwner() 69 | { 70 | return this->isOwner_; 71 | } 72 | 73 | inline Arg& Arg::setArray() 74 | { 75 | this->isArray_ = true; 76 | return *this; 77 | } 78 | 79 | inline bool Arg::isArray() 80 | { 81 | return this->isArray_; 82 | } 83 | } // Rice -------------------------------------------------------------------------------- /rice/Callback.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Callback__hpp_ 2 | #define Rice__Callback__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! Define a callback. 7 | /*! When C++ invokes a C style callback, Rice automatically converts the C++ arguments 8 | * to Ruby. However, there may be cases where you need to specify how individual arguments 9 | * should be handled. For example, callbacks often have a user data parameter which is 10 | * defined as a void pointer (void*). In this case, you need to tell Ruby that the parameter 11 | * is opaque and should not be convered. For example: 12 | * 13 | * define_callback(Arg("user_data").setOpaque()); 14 | * 15 | * \param args a list of Arg instance used to define default parameters (optional) 16 | * \return nothing 17 | */ 18 | template 19 | void define_callback(const Arg_Ts&...args); 20 | } 21 | #endif // Rice__Callback__hpp_ -------------------------------------------------------------------------------- /rice/Callback.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | template 4 | void define_callback(const Arg_Ts&...args) 5 | { 6 | MethodInfo* methodInfo = new MethodInfo(detail::function_traits::arity, args...); 7 | #ifdef HAVE_LIBFFI 8 | detail::NativeCallbackFFI::setMethodInfo(methodInfo); 9 | #else 10 | detail::NativeCallbackSimple::setMethodInfo(methodInfo); 11 | #endif 12 | } 13 | } -------------------------------------------------------------------------------- /rice/Constructor.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Constructor__hpp_ 2 | #define Rice__Constructor__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! Define a Type's Constructor and it's arguments. 7 | /*! E.g. for the default constructor on a Type: 8 | \code 9 | define_class() 10 | .define_constructor(Constructor()); 11 | \endcode 12 | * 13 | * The first template argument must be the type being wrapped. 14 | * Additional arguments must be the types of the parameters sent 15 | * to the constructor. 16 | * 17 | * For more information, see Rice::Data_Type::define_constructor. 18 | */ 19 | template 20 | class Constructor; 21 | } 22 | #endif // Rice__Constructor__hpp_ -------------------------------------------------------------------------------- /rice/Constructor.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | template 4 | class Constructor 5 | { 6 | public: 7 | static constexpr std::size_t arity = sizeof...(Arg_Ts); 8 | 9 | static constexpr bool isCopyConstructor() 10 | { 11 | if constexpr (arity == 1) 12 | { 13 | using Arg_Types = std::tuple; 14 | using First_Arg_T = std::tuple_element_t<0, Arg_Types>; 15 | return (arity == 1 && 16 | std::is_lvalue_reference_v && 17 | std::is_same_v>); 18 | } 19 | else 20 | { 21 | return false; 22 | } 23 | } 24 | 25 | static constexpr bool isMoveConstructor() 26 | { 27 | if constexpr (arity == 1) 28 | { 29 | using Arg_Types = std::tuple; 30 | using First_Arg_T = std::tuple_element_t<0, Arg_Types>; 31 | return (arity == 1 && 32 | std::is_rvalue_reference_v && 33 | std::is_same_v>); 34 | } 35 | else 36 | { 37 | return false; 38 | } 39 | } 40 | 41 | static void initialize(VALUE self, Arg_Ts...args) 42 | { 43 | // Call C++ constructor 44 | T* data = new T(args...); 45 | detail::wrapConstructed(self, Data_Type::ruby_data_type(), data, true); 46 | } 47 | 48 | static void initialize_copy(VALUE self, const T& other) 49 | { 50 | // Call C++ copy constructor 51 | T* data = new T(other); 52 | detail::wrapConstructed(self, Data_Type::ruby_data_type(), data, true); 53 | } 54 | 55 | }; 56 | 57 | //! Special-case Constructor used when defining Directors. 58 | template 59 | class Constructor 60 | { 61 | public: 62 | static constexpr bool isCopyConstructor() 63 | { 64 | return false; 65 | } 66 | 67 | static constexpr bool isMoveConstructor() 68 | { 69 | return false; 70 | } 71 | 72 | static void initialize(Object self, Arg_Ts...args) 73 | { 74 | // Call C++ constructor 75 | T* data = new T(self, args...); 76 | detail::wrapConstructed(self.value(), Data_Type::ruby_data_type(), data, true); 77 | } 78 | }; 79 | } -------------------------------------------------------------------------------- /rice/Director.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Director__hpp_ 2 | #define Rice__Director__hpp_ 3 | 4 | namespace Rice 5 | { 6 | /** 7 | * A Director works exactly as a SWIG %director works (thus the name). 8 | * You use this class to help build proxy classes so that polymorphism 9 | * works from C++ into Ruby. See the main README for how this class works. 10 | */ 11 | class Director 12 | { 13 | public: 14 | //! Construct new Director. Needs the Ruby object so that the 15 | // proxy class can call methods on that object. 16 | Director(Object self) : self_(self) 17 | { 18 | } 19 | 20 | virtual ~Director() = default; 21 | 22 | //! Raise a ruby exception when a call comes through for a pure virtual method 23 | /*! If a Ruby script calls 'super' on a method that's otherwise a pure virtual 24 | * method, use this method to throw an exception in this case. 25 | */ 26 | void raisePureVirtual() const 27 | { 28 | rb_raise(rb_eNotImpError, "Cannot call super() into a pure-virtual C++ method"); 29 | } 30 | 31 | //! Get the Ruby object linked to this C++ instance 32 | Object getSelf() const { return self_; } 33 | 34 | private: 35 | 36 | // Save the Ruby object related to the instance of this class 37 | Object self_; 38 | 39 | }; 40 | } 41 | #endif // Rice__Director__hpp_ 42 | -------------------------------------------------------------------------------- /rice/Enum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Enum__hpp_ 2 | #define Rice__Enum__hpp_ 3 | 4 | namespace Rice 5 | { 6 | /*! 7 | * \example enum/sample_enum.cpp 8 | */ 9 | 10 | //! A wrapper for enumerated types. 11 | /*! Provides a simple type-safe wrapper for enumerated types. At the 12 | * ruby level, the class will have convenience methods for iterating 13 | * over all the defined enum values, converting the values to strings, 14 | * and more. 15 | * 16 | * \param Enum_T the enumerated type 17 | * 18 | * Example: 19 | * \code 20 | * enum Color { Red, Green, Blue }; 21 | * Enum rb_cColor = define_enum("Color") 22 | * .define_value("Red", Red) 23 | * .define_value("Green", Green) 24 | * .define_value("Blue", Blue); 25 | * \endcode 26 | */ 27 | template 28 | class Enum : public Data_Type 29 | { 30 | using Underlying_T = std::underlying_type_t; 31 | 32 | public: 33 | Enum() = default; 34 | 35 | //! Construct and initialize. 36 | Enum(char const* name, Module module = rb_cObject); 37 | 38 | //! Define a new enum value. 39 | /*! \param name the name of the enum value. 40 | * \param value the value to associate with name. 41 | * \return *this 42 | */ 43 | Enum& define_value(std::string name, Enum_T value); 44 | 45 | //! Maps an enum value to the correct Ruby object 46 | /*! \param klass The bound Ruby class 47 | * \param enumValue The enum value 48 | * \return Object - The Ruby wrapper */ 49 | static Object from_enum(Class klass, Enum_T enumValue); 50 | 51 | private: 52 | void define_methods(Data_Type klass); 53 | 54 | static inline std::map valuesToNames_; 55 | }; 56 | 57 | template 58 | Enum define_enum(char const* name); 59 | 60 | template 61 | Enum define_enum_under(char const* name, Module module ); 62 | } // namespace Rice 63 | 64 | #endif // Rice__Enum__hpp_ -------------------------------------------------------------------------------- /rice/Exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Exception__hpp_ 2 | #define Rice__Exception__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice 7 | { 8 | //! A placeholder for Ruby exceptions. 9 | /*! You can use this to safely throw a Ruby exception using C++ syntax: 10 | * \code 11 | * VALUE foo(VALUE self) { 12 | * RUBY_TRY { 13 | * throw Rice::Exception(rb_eMyException, "uh oh!"); 14 | * RUBY_CATCH 15 | * } 16 | * \endcode 17 | */ 18 | class Exception 19 | : public std::exception 20 | { 21 | public: 22 | //! Construct a Exception with a Ruby exception instance 23 | explicit Exception(VALUE exception); 24 | 25 | //! Construct a Exception with printf-style formatting. 26 | /*! \param exc either an exception object or a class that inherits 27 | * from Exception. 28 | * \param fmt a printf-style format string 29 | * \param ... the arguments to the format string. 30 | */ 31 | template 32 | Exception(const Exception& other, char const* fmt, Arg_Ts&&...args); 33 | 34 | //! Construct a Exception with printf-style formatting. 35 | /*! \param exc either an exception object or a class that inherits 36 | * from Exception. 37 | * \param fmt a printf-style format string 38 | * \param ... the arguments to the format string. 39 | */ 40 | template 41 | Exception(const VALUE exceptionType, char const* fmt, Arg_Ts&&...args); 42 | 43 | //! Destructor 44 | virtual ~Exception() noexcept = default; 45 | 46 | //! Get message as a char const *. 47 | /*! If message is a non-string object, then this function will attempt 48 | * to throw an exception (which it can't do because of the no-throw 49 | * specification). 50 | * \return the underlying C pointer of the underlying message object. 51 | */ 52 | virtual char const* what() const noexcept override; 53 | 54 | //! Returns the Ruby exception class 55 | VALUE class_of() const; 56 | 57 | //! Returns an instance of a Ruby exception 58 | VALUE value() const; 59 | 60 | private: 61 | // TODO: Do we need to tell the Ruby gc about an exception instance? 62 | mutable VALUE exception_ = Qnil; 63 | mutable std::string message_; 64 | }; 65 | } // namespace Rice 66 | 67 | #endif // Rice__Exception__hpp_ -------------------------------------------------------------------------------- /rice/Exception.ipp: -------------------------------------------------------------------------------- 1 | 2 | namespace Rice 3 | { 4 | inline Exception::Exception(VALUE exception) : exception_(exception) 5 | { 6 | } 7 | 8 | template 9 | inline Exception::Exception(const Exception& other, char const* fmt, Arg_Ts&&...args) 10 | : Exception(other.class_of(), fmt, std::forward(args)...) 11 | { 12 | } 13 | 14 | template 15 | inline Exception::Exception(const VALUE exceptionClass, char const* fmt, Arg_Ts&&...args) 16 | { 17 | #if defined(__GNUC__) || defined(__clang__) 18 | #pragma GCC diagnostic push 19 | #pragma GCC diagnostic ignored "-Wformat-security" 20 | #endif 21 | 22 | size_t size = std::snprintf(nullptr, 0, fmt, std::forward(args)...); 23 | this->message_ = std::string(size, '\0'); 24 | 25 | // size+1 avoids truncating the string. Otherwise snprintf writes n - 1 characters 26 | // to allow space for null character but we don't need that since std::string 27 | // will add a null character internally at n + 1 28 | std::snprintf(&this->message_[0], size + 1, fmt, std::forward(args)...); 29 | 30 | #if defined(__GNUC__) || defined(__clang__) 31 | #pragma GCC diagnostic pop 32 | #endif 33 | 34 | // Now create the Ruby exception 35 | this->exception_ = detail::protect(rb_exc_new2, exceptionClass, this->message_.c_str()); 36 | } 37 | 38 | inline char const* Exception::what() const noexcept 39 | { 40 | if (this->message_.empty()) 41 | { 42 | // This isn't protected because if it fails then either we could eat the exception 43 | // (not good) or crash the program (better) 44 | VALUE rubyMessage = rb_funcall(this->exception_, rb_intern("message"), 0); 45 | this->message_ = std::string(RSTRING_PTR(rubyMessage), RSTRING_LEN(rubyMessage)); 46 | } 47 | return this->message_.c_str(); 48 | } 49 | 50 | inline VALUE Exception::class_of() const 51 | { 52 | return detail::protect(rb_class_of, this->exception_); 53 | } 54 | 55 | inline VALUE Exception::value() const 56 | { 57 | return this->exception_; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rice/Init.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Init__hpp_ 2 | #define Rice__Init__hpp_ 3 | 4 | namespace Rice 5 | { 6 | void init(); 7 | } 8 | #endif // Rice__Init__hpp_ -------------------------------------------------------------------------------- /rice/Init.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline void init() 4 | { 5 | detail::define_ruby_types(); 6 | define_fundamental_buffer_types(); 7 | }; 8 | } -------------------------------------------------------------------------------- /rice/JumpException.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__JumpException__hpp_ 2 | #define Rice__JumpException__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A placeholder for Ruby longjmp data. 7 | /*! When a Ruby exception is caught, the tag used for the longjmp is stored in 8 | * a Jump_Tag, then later passed to rb_jump_tag() when there is no more 9 | * C++ code to pass over. 10 | */ 11 | class JumpException : public std::exception 12 | { 13 | public: 14 | // Copied from vm_core.h 15 | enum ruby_tag_type { 16 | RUBY_TAG_NONE = 0x0, 17 | RUBY_TAG_RETURN = 0x1, 18 | RUBY_TAG_BREAK = 0x2, 19 | RUBY_TAG_NEXT = 0x3, 20 | RUBY_TAG_RETRY = 0x4, 21 | RUBY_TAG_REDO = 0x5, 22 | RUBY_TAG_RAISE = 0x6, 23 | RUBY_TAG_THROW = 0x7, 24 | RUBY_TAG_FATAL = 0x8, 25 | RUBY_TAG_MASK = 0xf 26 | }; 27 | 28 | public: 29 | JumpException(ruby_tag_type tag); 30 | virtual const char* what() const noexcept override; 31 | 32 | public: 33 | //! The tag being held. 34 | ruby_tag_type tag; 35 | 36 | private: 37 | void createMessage(); 38 | 39 | private: 40 | std::string message_; 41 | }; 42 | } // namespace Rice 43 | 44 | #endif // Rice__JumpException__hpp_ -------------------------------------------------------------------------------- /rice/JumpException.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline JumpException::JumpException(ruby_tag_type tag) : tag(tag) 4 | { 5 | this->createMessage(); 6 | } 7 | 8 | inline const char* JumpException::what() const noexcept 9 | { 10 | return this->message_.c_str(); 11 | } 12 | 13 | inline void JumpException::createMessage() 14 | { 15 | switch (this->tag) 16 | { 17 | case RUBY_TAG_NONE: 18 | this->message_ = "No error"; 19 | break; 20 | case RUBY_TAG_RETURN: 21 | this->message_ = "Unexpected return"; 22 | break; 23 | case RUBY_TAG_NEXT: 24 | this->message_ = "Unexpected next"; 25 | break; 26 | case RUBY_TAG_BREAK: 27 | this->message_ = "Unexpected break"; 28 | break; 29 | case RUBY_TAG_REDO: 30 | this->message_ = "Unexpected redo"; 31 | break; 32 | case RUBY_TAG_RETRY: 33 | this->message_ = "Retry outside of rescue clause"; 34 | break; 35 | case RUBY_TAG_THROW: 36 | this->message_ = "Unexpected throw"; 37 | case RUBY_TAG_RAISE: 38 | this->message_ = "Ruby exception was thrown"; 39 | break; 40 | case RUBY_TAG_FATAL: 41 | this->message_ = "Fatal error"; 42 | break; 43 | case RUBY_TAG_MASK: 44 | this->message_ = "Mask error"; 45 | break; 46 | } 47 | } 48 | } // namespace Rice 49 | -------------------------------------------------------------------------------- /rice/MemoryView.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__MemoryView__hpp_ 2 | #define Rice__MemoryView__hpp_ 3 | 4 | namespace Rice 5 | { 6 | class MemoryView 7 | { 8 | }; 9 | } 10 | 11 | #endif // Rice__MemoryView__hpp_ -------------------------------------------------------------------------------- /rice/MemoryView.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | } -------------------------------------------------------------------------------- /rice/Return.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Return__hpp_ 2 | #define Rice__Return__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! Helper for defining Return argument of a method 7 | 8 | class Return: public Arg 9 | { 10 | public: 11 | Return(); 12 | Return& keepAlive() override; 13 | Return& setValue() override; 14 | Return& setOpaque() override; 15 | Return& takeOwnership() override; 16 | Return& setArray() override; 17 | }; 18 | } // Rice 19 | 20 | #endif // Rice__Return__hpp_ 21 | -------------------------------------------------------------------------------- /rice/Return.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice 4 | { 5 | inline Return::Return(): Arg("Return") 6 | { 7 | } 8 | 9 | inline Return& Return::keepAlive() 10 | { 11 | Arg::keepAlive(); 12 | return *this; 13 | } 14 | 15 | inline Return& Return::setValue() 16 | { 17 | Arg::setValue(); 18 | return *this; 19 | } 20 | 21 | inline Return& Return::setOpaque() 22 | { 23 | Arg::setOpaque(); 24 | return *this; 25 | } 26 | 27 | inline Return& Return::takeOwnership() 28 | { 29 | Arg::takeOwnership(); 30 | return *this; 31 | } 32 | 33 | inline Return& Return::setArray() 34 | { 35 | Arg::setArray(); 36 | return *this; 37 | } 38 | } // Rice 39 | -------------------------------------------------------------------------------- /rice/cpp_api/Builtin_Object.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Builtin_Object__hpp_ 2 | #define Rice__Builtin_Object__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A smartpointer-like wrapper for Ruby builtin objects. 7 | /*! A builtin object is one of Ruby's internal types, e.g. RArray or 8 | * RString. Every builtin type structure has a corresponding integer 9 | * type number (e.g T_ARRAY for RArray or T_STRING for RString). This 10 | * class is a wrapper for those types of objects, primarily useful as a 11 | * base class for other wrapper classes like Array and Hash. 12 | */ 13 | template 14 | class Builtin_Object 15 | : public Object 16 | { 17 | public: 18 | //! Wrap an already allocated Ruby object. 19 | /*! Checks to see if the object is an object of type Builtin_Type; a 20 | * C++ exception is thrown if this is not the case. 21 | * \param value the object to be wrapped. 22 | */ 23 | Builtin_Object(Object value); 24 | 25 | RObject& operator*() const; //!< Return a reference to obj_ 26 | RObject* operator->() const; //!< Return a pointer to obj_ 27 | RObject* get() const; //!< Return a pointer to obj_ 28 | }; 29 | } // namespace Rice 30 | 31 | #endif // Rice__Builtin_Object__hpp_ -------------------------------------------------------------------------------- /rice/cpp_api/Builtin_Object.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice 4 | { 5 | namespace detail 6 | { 7 | inline VALUE check_type(Object value, int type) 8 | { 9 | detail::protect(rb_check_type, value.value(), type); 10 | return Qnil; 11 | } 12 | } 13 | 14 | template 15 | inline Builtin_Object::Builtin_Object(Object value) : Object(value) 16 | { 17 | detail::check_type(value, Builtin_Type); 18 | } 19 | 20 | template 21 | inline RObject& Builtin_Object::operator*() const 22 | { 23 | return *ROBJECT(this->value()); 24 | } 25 | 26 | template 27 | inline RObject* Builtin_Object::operator->() const 28 | { 29 | return ROBJECT(this->value()); 30 | } 31 | 32 | template 33 | inline RObject* Builtin_Object::get() const 34 | { 35 | return ROBJECT(this->value()); 36 | } 37 | } // namespace Rice 38 | -------------------------------------------------------------------------------- /rice/cpp_api/Class.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Class__hpp_ 2 | #define Rice__Class__hpp_ 3 | 4 | /*! 5 | * \example inheritance/animals.cpp 6 | * \example callbacks/sample_callbacks.cpp 7 | */ 8 | 9 | namespace Rice 10 | { 11 | //! A helper for defining a Class and its methods. 12 | /*! This class provides a C++-style interface to ruby's Class class and 13 | * for defining methods on that class. 14 | */ 15 | class Class: public Module 16 | { 17 | public: 18 | //! Default construct a new class wrapper and initialize it to 19 | //! rb_cObject. 20 | Class() = default; 21 | 22 | //! Construct a new class wrapper from a ruby object of type T_CLASS. 23 | Class(VALUE v); 24 | 25 | //! Disallow creation of an instance from Ruby code. 26 | /*! Undefines the singleton method allocate (or new, if using a 27 | * version of ruby prior to 1.7) and the instance method initialize. 28 | */ 29 | Class & undef_creation_funcs(); 30 | 31 | // Create a new instance 32 | template 33 | Object create(Arg_Ts ...args); 34 | 35 | //! Class name 36 | /*! \return std::string. 37 | */ 38 | const std::string name() const; 39 | 40 | //! Base class name - does not include any parent modules 41 | /*! \return std::string. 42 | */ 43 | const std::string base_name() const; 44 | 45 | #include "shared_methods.hpp" 46 | }; 47 | 48 | //! Define a new class in the namespace given by module. 49 | /*! \param module the Module in which to define the class. 50 | * \param name the name of the class. 51 | * \param superclass the base class to use. 52 | * \return the new class. 53 | */ 54 | Class define_class_under(Object parent, char const * name, const Class& superclass = rb_cObject); 55 | Class define_class_under(Object parent, Identifier id, const Class& superclass = rb_cObject); 56 | 57 | //! Define a new class in the default namespace. 58 | /*! \param name the name of the class. 59 | * \param superclass the base class to use. 60 | * \return the new class. 61 | */ 62 | Class define_class(char const * name, const Class& superclass = rb_cObject); 63 | 64 | //! Create a new anonymous class. 65 | /*! \return the new class. 66 | */ 67 | Class anonymous_class(); 68 | } // namespace Rice 69 | 70 | #endif // Rice__Class__hpp_ -------------------------------------------------------------------------------- /rice/cpp_api/Class.ipp: -------------------------------------------------------------------------------- 1 | 2 | namespace Rice 3 | { 4 | inline Class::Class(VALUE value) : Module(value) 5 | { 6 | detail::protect(rb_check_type, value, (int)T_CLASS); 7 | } 8 | 9 | inline Class& Class::undef_creation_funcs() 10 | { 11 | detail::protect(rb_undef_alloc_func, value()); 12 | detail::protect(rb_undef_method, value(), "initialize"); 13 | return *this; 14 | } 15 | 16 | template 17 | inline Object Class::create(Arg_Ts ...args) 18 | { 19 | return this->call("new", args...); 20 | } 21 | 22 | inline const std::string Class::name() const 23 | { 24 | const char* buffer = rb_class2name(this->value()); 25 | return std::string(buffer); 26 | } 27 | 28 | inline const std::string Class::base_name() const 29 | { 30 | std::string name = this->name(); 31 | auto regex = std::regex("^.*::"); 32 | std::string result = std::regex_replace(name, regex, ""); 33 | return result; 34 | } 35 | 36 | inline Class define_class_under(Object parent, Identifier id, const Class& superclass) 37 | { 38 | VALUE klass = detail::protect(rb_define_class_id_under, parent.value(), id, superclass.value()); 39 | 40 | // We MUST reset the instance registry in case the user just redefined a class which resets it 41 | detail::Registries::instance.natives.reset(klass); 42 | 43 | return klass; 44 | } 45 | 46 | inline Class define_class_under(Object parent, char const* name, const Class& superclass) 47 | { 48 | Identifier id(name); 49 | return define_class_under(parent, id, superclass); 50 | } 51 | 52 | inline Class define_class(char const* name, const Class& superclass) 53 | { 54 | VALUE klass = detail::protect(rb_define_class, name, superclass.value()); 55 | 56 | // We MUST reset the instance registry in case the user just redefined a class which resets it 57 | detail::Registries::instance.natives.reset(klass); 58 | 59 | return klass; 60 | } 61 | 62 | inline Class anonymous_class() 63 | { 64 | VALUE klass = detail::protect(rb_class_new, rb_cObject); 65 | VALUE singleton = detail::protect(rb_singleton_class, klass); 66 | 67 | // Ruby will reuse addresses previously assigned to other modules 68 | // that have subsequently been garbage collected 69 | detail::Registries::instance.natives.reset(klass); 70 | detail::Registries::instance.natives.reset(singleton); 71 | 72 | return klass; 73 | } 74 | } 75 | 76 | namespace Rice::detail 77 | { 78 | template<> 79 | class To_Ruby 80 | { 81 | public: 82 | static VALUE convert(Class const& x) 83 | { 84 | return x.value(); 85 | } 86 | }; 87 | 88 | template<> 89 | class From_Ruby 90 | { 91 | public: 92 | Class convert(VALUE value) 93 | { 94 | return Class(value); 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /rice/cpp_api/Encoding.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Encoding__hpp_ 2 | #define Rice__Encoding__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A wrapper for a Ruby encoding 7 | class Encoding 8 | { 9 | public: 10 | static Encoding utf8(); 11 | 12 | //! Wrap an existing encoding. 13 | Encoding(rb_encoding* encoding); 14 | 15 | //! Implicit conversion to VALUE. 16 | operator rb_encoding* () const 17 | { 18 | return this->encoding_; 19 | } 20 | 21 | operator VALUE () const 22 | { 23 | return detail::protect(rb_enc_from_encoding, this->encoding_); 24 | } 25 | 26 | private: 27 | rb_encoding* encoding_; 28 | }; 29 | } // namespace Rice 30 | 31 | #endif // Rice__Encoding__hpp_ 32 | 33 | -------------------------------------------------------------------------------- /rice/cpp_api/Encoding.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline Encoding Encoding::utf8() 4 | { 5 | return Encoding(rb_utf8_encoding()); 6 | } 7 | 8 | inline Encoding::Encoding(rb_encoding* encoding) : encoding_(encoding) 9 | { 10 | } 11 | } 12 | 13 | /*namespace Rice::detail 14 | { 15 | template<> 16 | struct Type 17 | { 18 | static bool verify() 19 | { 20 | return true; 21 | } 22 | }; 23 | 24 | template<> 25 | class To_Ruby 26 | { 27 | public: 28 | VALUE convert(const Encoding& encoding) 29 | { 30 | // return x.value(); 31 | } 32 | }; 33 | 34 | template<> 35 | class From_Ruby 36 | { 37 | public: 38 | Convertible is_convertible(VALUE value) 39 | { 40 | switch (rb_type(value)) 41 | { 42 | case RUBY_T_SYMBOL: 43 | return Convertible::Exact; 44 | break; 45 | case RUBY_T_STRING: 46 | return Convertible::Cast; 47 | break; 48 | default: 49 | return Convertible::None; 50 | } 51 | } 52 | 53 | Encoding convert(VALUE value) 54 | { 55 | // return Symbol(value); 56 | } 57 | }; 58 | } 59 | */ -------------------------------------------------------------------------------- /rice/cpp_api/Identifier.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Identifier__hpp_ 2 | #define Rice__Identifier__hpp_ 3 | 4 | namespace Rice 5 | { 6 | class Symbol; 7 | 8 | //! A wrapper for the ID type 9 | /*! An ID is ruby's internal representation of a Symbol object. 10 | */ 11 | class Identifier 12 | { 13 | public: 14 | //! Construct a new Identifier from an ID. 15 | Identifier(ID id); 16 | 17 | //! Construct a new Identifier from a Symbol. 18 | Identifier(Symbol const& symbol); 19 | 20 | //! Construct a new Identifier from a c string. 21 | Identifier(char const* s, Encoding encoding = Encoding::utf8()); 22 | 23 | //! Construct a new Identifier from a string. 24 | Identifier(std::string const& string, Encoding encoding = Encoding::utf8()); 25 | 26 | //! Return a string representation of the Identifier. 27 | char const* c_str() const; 28 | 29 | //! Return a string representation of the Identifier. 30 | std::string str() const; 31 | 32 | //! Return the underlying ID 33 | ID id() const { return id_; } 34 | 35 | //! Return the underlying ID 36 | operator ID() const { return id_; } 37 | 38 | //! Return the ID as a Symbol 39 | VALUE to_sym() const; 40 | 41 | private: 42 | ID id_; 43 | }; 44 | } // namespace Rice 45 | 46 | #endif // Rice__Identifier__hpp_ 47 | -------------------------------------------------------------------------------- /rice/cpp_api/Identifier.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline Identifier::Identifier(ID id) : id_(id) 4 | { 5 | } 6 | 7 | inline Identifier::Identifier(char const* name, Encoding encoding) 8 | { 9 | this->id_ = detail::protect(rb_intern3, name, (long)strlen(name), encoding); 10 | } 11 | 12 | inline Identifier::Identifier(const std::string& name, Encoding encoding) 13 | { 14 | this->id_ = detail::protect(rb_intern3, name.c_str(), (long)name.size(), encoding); 15 | } 16 | 17 | inline char const* Identifier::c_str() const 18 | { 19 | return detail::protect(rb_id2name, id_); 20 | } 21 | 22 | inline std::string Identifier::str() const 23 | { 24 | return c_str(); 25 | } 26 | 27 | inline VALUE Identifier::to_sym() const 28 | { 29 | return ID2SYM(id_); 30 | } 31 | } -------------------------------------------------------------------------------- /rice/cpp_api/Module.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Module__hpp_ 2 | #define Rice__Module__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | void validateType(); 8 | 9 | //! A helper for defining a Module and its methods. 10 | /*! This class provides a C++-style interface to ruby's Module class and 11 | * for defining methods on that module. 12 | * 13 | * Many of the methods are defined in Module_impl.hpp so that they can 14 | * return a reference to the most derived type. 15 | */ 16 | // TODO: we can't inherit from Builtin_Object, because Class needs 17 | // type T_CLASS and Module needs type T_MODULE 18 | class Module : public Object 19 | { 20 | public: 21 | //! Default construct a Module and initialize it to rb_cObject. 22 | Module(); 23 | 24 | //! Construct a Module from an existing Module object. 25 | Module(VALUE v); 26 | 27 | //! Construct a Module from an string that references a Module 28 | Module(std::string name, Object under = rb_cObject); 29 | 30 | //! Return the name of the module. 31 | String name() const; 32 | 33 | //! Return an array containing the Module's ancestors. 34 | /*! You will need to include Array.hpp to use this function. 35 | */ 36 | Array ancestors() const; 37 | 38 | //! Return the module's singleton class. 39 | /*! You will need to include Class.hpp to use this function. 40 | */ 41 | Class singleton_class() const; 42 | 43 | //! Evaluate the given string in the context of the module. 44 | /*! This is equivalant to calling obj.module_eval(s) from inside the 45 | * interpreter. 46 | * \return the result of the expression. 47 | */ 48 | Object module_eval(String const& s); 49 | 50 | #include "shared_methods.hpp" 51 | protected: 52 | template 53 | void wrap_native_call(VALUE klass, std::string name, Function_T&& function, MethodInfo* methodInfo); 54 | }; 55 | 56 | //! Define a new module in the namespace given by module. 57 | /*! \param module the module in which to define the new module. 58 | * \param name the name of the new module. 59 | */ 60 | Module define_module_under(Object module, char const * name); 61 | 62 | //! Define a new module in the default namespace. 63 | /*! \param name the name of the new module. 64 | */ 65 | Module define_module(char const * name); 66 | 67 | //! Create a new anonymous module. 68 | /*! \return the new module. 69 | */ 70 | Module anonymous_module(); 71 | } 72 | #endif // Rice__Module__hpp_ -------------------------------------------------------------------------------- /rice/cpp_api/String.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__String__hpp_ 2 | #define Rice__String__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A Wraper for the ruby String class. 7 | /*! This class provides a C++-style interface to ruby's String class and 8 | * its associated rb_str_* functions. 9 | * 10 | * Example: 11 | * \code 12 | * String s(String::format("%s: %d", "foo", 42)); 13 | * std::cout << s.length() << std::endl; 14 | * \endcode 15 | */ 16 | class String 17 | : public Builtin_Object 18 | { 19 | public: 20 | //! Construct a new string. 21 | String(); 22 | 23 | //! Wrap an existing string. 24 | String(VALUE v); 25 | 26 | //! Wrap an existing string. 27 | String(Object v); 28 | 29 | //! Construct a String from an Identifier. 30 | String(Identifier id); 31 | 32 | //! Construct a String from a null-terminated C string. 33 | String(char const* s); 34 | 35 | //! Construct a String from an std::string. 36 | String(std::string const& s); 37 | 38 | //! Construct a String from an std::string_view. 39 | String(std::string_view const& s); 40 | 41 | //! Format a string using printf-style formatting. 42 | template 43 | static inline String format(char const* fmt, Arg_Ts&&...args); 44 | 45 | //! Get the length of the String. 46 | /*! \return the length of the string. 47 | */ 48 | size_t length() const; 49 | 50 | //! Get the character at the given index. 51 | /*! \param index the desired index. 52 | * \return the character at the given index. 53 | */ 54 | char operator[](ptrdiff_t index) const; 55 | 56 | //! Return a pointer to the beginning of the underlying C string. 57 | char const* c_str() const; 58 | 59 | //! Return a copy of the string as an std::string. 60 | std::string str() const; 61 | 62 | //! Return an array from a string by unpacking it 63 | template 64 | Array unpack() const; 65 | 66 | //! Create an Identifier from the String. 67 | /*! Calls rb_intern to create an ID. 68 | * \return an Identifier holding the ID returned from rb_intern. 69 | */ 70 | Identifier intern() const; 71 | }; 72 | } // namespace Rice 73 | 74 | #endif // Rice__String__hpp_ -------------------------------------------------------------------------------- /rice/cpp_api/Struct.ipp: -------------------------------------------------------------------------------- 1 | 2 | namespace Rice 3 | { 4 | inline Struct& Struct::initialize(Module module, Identifier name) 5 | { 6 | Class struct_class(rb_cStruct); 7 | 8 | Object type = struct_class.vcall("new", this->members()); 9 | 10 | set_value(type); 11 | module.const_set(name, type); 12 | 13 | return *this; 14 | } 15 | 16 | inline Struct& Struct::define_member(Identifier name) 17 | { 18 | if (value() != rb_cObject) 19 | { 20 | throw std::runtime_error("struct is already initialized"); 21 | } 22 | 23 | members_.push_back(name.to_sym()); 24 | 25 | return *this; 26 | } 27 | 28 | inline Array Struct::members() const 29 | { 30 | if (value() == rb_cObject) 31 | { 32 | // Struct is not yet defined 33 | return Array(members_.begin(), members_.end()); 34 | } 35 | else 36 | { 37 | // Struct is defined, call Ruby API 38 | return rb_struct_s_members(this->value()); 39 | } 40 | } 41 | 42 | inline Struct::Instance Struct::new_instance(Array args) const 43 | { 44 | Object instance = const_cast(this)->vcall("new", args); 45 | return Instance(*this, instance); 46 | } 47 | 48 | inline Struct::Instance::Instance(Struct const& type, Array args) : 49 | Builtin_Object(type.new_instance(args)), type_(type) 50 | { 51 | } 52 | 53 | inline Struct::Instance::Instance(Struct const& type, Object s) : 54 | Builtin_Object(s), type_(type) 55 | { 56 | } 57 | 58 | inline Struct define_struct() 59 | { 60 | return Struct(); 61 | } 62 | 63 | template 64 | inline Object Struct::Instance::operator[](T index) 65 | { 66 | return rb_struct_aref(value(), ULONG2NUM(index)); 67 | } 68 | 69 | template<> 70 | inline Object Struct::Instance::operator[](Identifier member) 71 | { 72 | return rb_struct_aref(value(), Symbol(member)); 73 | } 74 | 75 | template<> 76 | inline Object Struct::Instance::operator[](char const* name) 77 | { 78 | return (*this)[Identifier(name)]; 79 | } 80 | } 81 | 82 | namespace Rice::detail 83 | { 84 | template<> 85 | struct Type 86 | { 87 | static bool verify() 88 | { 89 | return true; 90 | } 91 | }; 92 | } -------------------------------------------------------------------------------- /rice/cpp_api/Symbol.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Symbol__hpp_ 2 | #define Rice__Symbol__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! A wrapper for ruby's Symbol class. 7 | /*! Symbols are internal identifiers in ruby. They are singletons and 8 | * can be thought of as frozen strings. They differ from an Identifier 9 | * in that they are in fact real Objects, but they can be converted 10 | * back and forth between Identifier and Symbol. 11 | */ 12 | class Symbol 13 | : public Object 14 | { 15 | public: 16 | //! Wrap an existing symbol. 17 | Symbol(VALUE v); 18 | 19 | //! Wrap an existing symbol. 20 | Symbol(Object v); 21 | 22 | //! Construct a Symbol from an Identifier. 23 | Symbol(Identifier id); 24 | 25 | //! Construct a Symbol from a null-terminated C string. 26 | Symbol(char const* s = ""); 27 | 28 | //! Construct a Symbol from an std::string. 29 | Symbol(std::string const& s); 30 | 31 | //! Construct a Symbol from an std::string_view. 32 | Symbol(std::string_view const& s); 33 | 34 | //! Return a string representation of the Symbol. 35 | char const* c_str() const; 36 | 37 | //! Return a string representation of the Symbol. 38 | std::string str() const; 39 | 40 | //! Return the Symbol as an Identifier. 41 | Identifier to_id() const; 42 | }; 43 | } // namespace Rice 44 | 45 | #endif // Rice__Symbol__hpp_ 46 | 47 | -------------------------------------------------------------------------------- /rice/cpp_api/Symbol.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice 2 | { 3 | inline Symbol::Symbol(VALUE value) : Object(value) 4 | { 5 | detail::protect(rb_check_type, value, (int)T_SYMBOL); 6 | } 7 | 8 | inline Symbol::Symbol(Object value) : Object(value) 9 | { 10 | detail::protect(rb_check_type, value.value(), (int)T_SYMBOL); 11 | } 12 | 13 | inline Symbol::Symbol(char const* s) 14 | : Object(detail::protect(rb_id2sym, detail::protect(rb_intern, s))) 15 | { 16 | } 17 | 18 | inline Symbol::Symbol(std::string const& s) 19 | : Object(detail::protect(rb_id2sym, detail::protect(rb_intern2, s.c_str(), (long)s.length()))) 20 | { 21 | } 22 | 23 | inline Symbol::Symbol(std::string_view const& view) 24 | : Object(detail::protect(rb_id2sym, detail::protect(rb_intern2, view.data(), (long)view.length()))) 25 | { 26 | } 27 | 28 | inline Symbol::Symbol(Identifier id) : Object(detail::protect(rb_id2sym, id)) 29 | { 30 | } 31 | 32 | inline char const* Symbol::c_str() const 33 | { 34 | return to_id().c_str(); 35 | } 36 | 37 | inline std::string Symbol::str() const 38 | { 39 | return to_id().str(); 40 | } 41 | 42 | inline Identifier Symbol::to_id() const 43 | { 44 | return rb_to_id(value()); 45 | } 46 | } 47 | 48 | namespace Rice::detail 49 | { 50 | template<> 51 | struct Type 52 | { 53 | static bool verify() 54 | { 55 | return true; 56 | } 57 | }; 58 | 59 | template<> 60 | class To_Ruby 61 | { 62 | public: 63 | VALUE convert(Symbol const& x) 64 | { 65 | return x.value(); 66 | } 67 | }; 68 | 69 | template<> 70 | class From_Ruby 71 | { 72 | public: 73 | Convertible is_convertible(VALUE value) 74 | { 75 | switch (rb_type(value)) 76 | { 77 | case RUBY_T_SYMBOL: 78 | return Convertible::Exact; 79 | break; 80 | case RUBY_T_STRING: 81 | return Convertible::Cast; 82 | break; 83 | default: 84 | return Convertible::None; 85 | } 86 | } 87 | 88 | Symbol convert(VALUE value) 89 | { 90 | return Symbol(value); 91 | } 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /rice/detail/DefaultHandler.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__DefaultHandler__hpp_ 2 | #define Rice__detail__DefaultHandler__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | class DefaultHandler 7 | { 8 | public: 9 | void operator()() const; 10 | }; 11 | } 12 | #endif // Rice__detail__DefaultHandler__hpp_ -------------------------------------------------------------------------------- /rice/detail/DefaultHandler.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | inline void Rice::detail::DefaultHandler::operator()() const 4 | { 5 | // This handler does nothing, it just rethrows the exception so it can be handled 6 | throw; 7 | } 8 | } -------------------------------------------------------------------------------- /rice/detail/HandlerRegistry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__HandlerRegistry__hpp_ 2 | #define Rice__detail__HandlerRegistry__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | class HandlerRegistry 9 | { 10 | public: 11 | HandlerRegistry(); 12 | void set(std::function handler); 13 | std::function handler() const; 14 | 15 | private: 16 | std::function handler_; 17 | }; 18 | } // namespace Rice::detail 19 | 20 | #endif // Rice__detail__HandlerRegistry__hpp_ 21 | 22 | -------------------------------------------------------------------------------- /rice/detail/HandlerRegistry.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | inline HandlerRegistry::HandlerRegistry() : handler_(DefaultHandler()) 4 | { 5 | } 6 | 7 | inline void HandlerRegistry::set(std::function handler) 8 | { 9 | this->handler_ = handler; 10 | } 11 | 12 | inline std::function HandlerRegistry::handler() const 13 | { 14 | return this->handler_; 15 | } 16 | } -------------------------------------------------------------------------------- /rice/detail/InstanceRegistry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__InstanceRegistry__hpp_ 2 | #define Rice__detail__InstanceRegistry__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | class InstanceRegistry 9 | { 10 | public: 11 | template 12 | VALUE lookup(T& cppInstance); 13 | 14 | template 15 | VALUE lookup(T* cppInstance); 16 | VALUE lookup(void* cppInstance); 17 | 18 | void add(void* cppInstance, VALUE rubyInstance); 19 | void remove(void* cppInstance); 20 | void clear(); 21 | 22 | public: 23 | bool isEnabled = false; 24 | 25 | private: 26 | std::map objectMap_; 27 | }; 28 | } // namespace Rice::detail 29 | 30 | #endif // Rice__detail__InstanceRegistry__hpp_ 31 | 32 | -------------------------------------------------------------------------------- /rice/detail/InstanceRegistry.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template 6 | inline VALUE InstanceRegistry::lookup(T& cppInstance) 7 | { 8 | return this->lookup((void*)&cppInstance); 9 | } 10 | 11 | template 12 | inline VALUE InstanceRegistry::lookup(T* cppInstance) 13 | { 14 | return this->lookup((void*)cppInstance); 15 | } 16 | 17 | inline VALUE InstanceRegistry::lookup(void* cppInstance) 18 | { 19 | if (!this->isEnabled) 20 | return Qnil; 21 | 22 | auto it = this->objectMap_.find(cppInstance); 23 | if (it != this->objectMap_.end()) 24 | { 25 | return it->second; 26 | } 27 | else 28 | { 29 | return Qnil; 30 | } 31 | } 32 | 33 | inline void InstanceRegistry::add(void* cppInstance, VALUE rubyInstance) 34 | { 35 | if (this->isEnabled) 36 | { 37 | this->objectMap_[cppInstance] = rubyInstance; 38 | } 39 | } 40 | 41 | inline void InstanceRegistry::remove(void* cppInstance) 42 | { 43 | this->objectMap_.erase(cppInstance); 44 | } 45 | 46 | inline void InstanceRegistry::clear() 47 | { 48 | this->objectMap_.clear(); 49 | } 50 | } // namespace 51 | -------------------------------------------------------------------------------- /rice/detail/MethodInfo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__MethodInfo__hpp_ 2 | #define Rice__MethodInfo__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice 7 | { 8 | class MethodInfo 9 | { 10 | public: 11 | MethodInfo() = default; 12 | 13 | template 14 | MethodInfo(size_t argCount, const Arg_Ts&...args); 15 | 16 | /** 17 | * Add a defined Arg to this list of Arguments 18 | */ 19 | void addArg(const Arg& arg); 20 | 21 | /** 22 | * Get argument by position 23 | */ 24 | Arg* arg(size_t pos); 25 | 26 | /** 27 | * Get argument by name 28 | */ 29 | Arg* arg(std::string name); 30 | 31 | int argCount(); 32 | 33 | // Iterator support 34 | std::vector::iterator begin(); 35 | std::vector::iterator end(); 36 | 37 | Return returnInfo; 38 | 39 | private: 40 | template 41 | void processArg(const Arg_T& arg); 42 | 43 | std::vector args_; 44 | }; 45 | } 46 | #endif // Rice__MethodInfo__hpp_ -------------------------------------------------------------------------------- /rice/detail/MethodInfo.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice 4 | { 5 | template 6 | inline MethodInfo::MethodInfo(size_t argCount, const Arg_Ts&...args) 7 | { 8 | // Process the passed in arguments 9 | (this->processArg(args), ...); 10 | 11 | // Fill in any missing arguments 12 | for (size_t i = this->args_.size(); i < argCount; i++) 13 | { 14 | Arg arg("arg_" + std::to_string(i)); 15 | this->addArg(arg); 16 | } 17 | 18 | // TODO - so hacky but update the Arg positions 19 | for (uint32_t i = 0; i < this->args_.size(); i++) 20 | { 21 | this->args_[i].position = i; 22 | } 23 | } 24 | 25 | template 26 | inline void MethodInfo::processArg(const Arg_T& arg) 27 | { 28 | static_assert(std::is_same_v || std::is_same_v, "Unknown argument type"); 29 | 30 | if constexpr (std::is_same_v) 31 | { 32 | this->returnInfo = arg; 33 | } 34 | else if constexpr (std::is_same_v) 35 | { 36 | this->addArg(arg); 37 | } 38 | } 39 | 40 | inline void MethodInfo::addArg(const Arg& arg) 41 | { 42 | this->args_.push_back(arg); 43 | } 44 | 45 | inline int MethodInfo::argCount() 46 | { 47 | return this->args_.size(); 48 | } 49 | 50 | inline Arg* MethodInfo::arg(size_t pos) 51 | { 52 | if (pos < this->args_.size()) 53 | { 54 | return &this->args_[pos]; 55 | } 56 | else 57 | { 58 | return nullptr; 59 | } 60 | } 61 | 62 | inline Arg* MethodInfo::arg(std::string name) 63 | { 64 | for (Arg& arg : this->args_) 65 | { 66 | if (arg.name == name) 67 | { 68 | return &arg; 69 | } 70 | } 71 | return nullptr; 72 | } 73 | 74 | inline std::vector::iterator MethodInfo::begin() 75 | { 76 | return this->args_.begin(); 77 | } 78 | 79 | inline std::vector::iterator MethodInfo::end() 80 | { 81 | return this->args_.end(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /rice/detail/Native.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Native__hpp_ 2 | #define Rice__detail__Native__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | class Native; 7 | 8 | class Resolved 9 | { 10 | public: 11 | inline bool operator<(Resolved other); 12 | inline bool operator>(Resolved other); 13 | 14 | Convertible convertible; 15 | double parameterMatch; 16 | Native* native; 17 | }; 18 | 19 | class Native 20 | { 21 | public: 22 | static VALUE resolve(int argc, VALUE* argv, VALUE self); 23 | public: 24 | virtual ~Native() = default; 25 | VALUE call(int argc, VALUE* argv, VALUE self); 26 | 27 | virtual Resolved matches(size_t argc, const VALUE* argv, VALUE self) = 0; 28 | virtual VALUE operator()(size_t argc, const VALUE* argv, VALUE self) = 0; 29 | virtual std::string toString() = 0; 30 | }; 31 | } 32 | 33 | #endif // Rice__detail__Native__hpp_ 34 | -------------------------------------------------------------------------------- /rice/detail/NativeAttributeGet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Native_Attribute_Get__hpp_ 2 | #define Rice__detail__Native_Attribute_Get__hpp_ 3 | 4 | namespace Rice 5 | { 6 | enum class AttrAccess 7 | { 8 | ReadWrite, 9 | Read, 10 | Write 11 | }; 12 | 13 | namespace detail 14 | { 15 | template 16 | class NativeAttributeGet: Native 17 | { 18 | public: 19 | using NativeAttribute_T = NativeAttributeGet; 20 | 21 | using T = typename attribute_traits::attr_type; 22 | using Receiver_T = typename attribute_traits::class_type; 23 | using To_Ruby_T = remove_cv_recursive_t; 24 | 25 | public: 26 | // Register attribute getter with Ruby 27 | static void define(VALUE klass, std::string name, Attribute_T attribute); 28 | 29 | public: 30 | // Disallow creating/copying/moving 31 | NativeAttributeGet() = delete; 32 | NativeAttributeGet(const NativeAttribute_T&) = delete; 33 | NativeAttributeGet(NativeAttribute_T&&) = delete; 34 | void operator=(const NativeAttribute_T&) = delete; 35 | void operator=(NativeAttribute_T&&) = delete; 36 | 37 | Resolved matches(size_t argc, const VALUE* argv, VALUE self) override; 38 | VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override; 39 | std::string toString() override; 40 | 41 | protected: 42 | NativeAttributeGet(VALUE klass, std::string name, Attribute_T attr); 43 | 44 | private: 45 | VALUE klass_; 46 | std::string name_; 47 | Attribute_T attribute_; 48 | }; 49 | } // detail 50 | } // Rice 51 | 52 | #endif // Rice__detail__Native_Attribute_Get__hpp_ 53 | -------------------------------------------------------------------------------- /rice/detail/NativeAttributeGet.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | namespace Rice::detail 6 | { 7 | template 8 | void NativeAttributeGet::define(VALUE klass, std::string name, Attribute_T attribute) 9 | { 10 | // Create a NativeAttributeGet that Ruby will call to read/write C++ variables 11 | NativeAttribute_T* nativeAttribute = new NativeAttribute_T(klass, name, std::forward(attribute)); 12 | std::unique_ptr native(nativeAttribute); 13 | 14 | detail::protect(rb_define_method, klass, name.c_str(), (RUBY_METHOD_FUNC)&Native::resolve, -1); 15 | 16 | // Add to native registry. Since attributes cannot be overridden, there is no need to set the 17 | // matches or calls function pointer. Instead Ruby can call the static call method defined on 18 | // this class (&NativeAttribute_T::get). 19 | Identifier identifier(name); 20 | detail::Registries::instance.natives.add(klass, identifier.id(), native); 21 | } 22 | 23 | template 24 | inline Resolved NativeAttributeGet::matches(size_t argc, const VALUE* argv, VALUE self) 25 | { 26 | if (argc == 0) 27 | return Resolved { Convertible::Exact, 1, this }; 28 | else 29 | return Resolved{ Convertible::None, 0, this }; 30 | } 31 | 32 | template 33 | NativeAttributeGet::NativeAttributeGet(VALUE klass, std::string name, Attribute_T attribute) 34 | : klass_(klass), name_(name), attribute_(attribute) 35 | { 36 | } 37 | 38 | template 39 | inline VALUE NativeAttributeGet::operator()(size_t argc, const VALUE* argv, VALUE self) 40 | { 41 | if constexpr (std::is_member_object_pointer_v) 42 | { 43 | Receiver_T* nativeSelf = From_Ruby().convert(self); 44 | return To_Ruby().convert(nativeSelf->*attribute_); 45 | } 46 | else 47 | { 48 | return To_Ruby().convert(*attribute_); 49 | } 50 | } 51 | 52 | template 53 | inline std::string NativeAttributeGet::toString() 54 | { 55 | return ""; 56 | } 57 | } // Rice 58 | -------------------------------------------------------------------------------- /rice/detail/NativeAttributeSet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Native_Attribute_Set__hpp_ 2 | #define Rice__detail__Native_Attribute_Set__hpp_ 3 | 4 | namespace Rice 5 | { 6 | namespace detail 7 | { 8 | template 9 | class NativeAttributeSet: Native 10 | { 11 | public: 12 | using NativeAttribute_T = NativeAttributeSet; 13 | using Attr_T = typename attribute_traits::attr_type; 14 | using T_Unqualified = remove_cv_recursive_t; 15 | using Receiver_T = typename attribute_traits::class_type; 16 | 17 | public: 18 | // Register attribute getter/setter with Ruby 19 | static void define(VALUE klass, std::string name, Attribute_T attribute); 20 | 21 | public: 22 | // Disallow creating/copying/moving 23 | NativeAttributeSet() = delete; 24 | NativeAttributeSet(const NativeAttribute_T&) = delete; 25 | NativeAttributeSet(NativeAttribute_T&&) = delete; 26 | void operator=(const NativeAttribute_T&) = delete; 27 | void operator=(NativeAttribute_T&&) = delete; 28 | 29 | Resolved matches(size_t argc, const VALUE* argv, VALUE self) override; 30 | VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override; 31 | std::string toString() override; 32 | 33 | protected: 34 | NativeAttributeSet(VALUE klass, std::string name, Attribute_T attr); 35 | 36 | private: 37 | VALUE klass_; 38 | std::string name_; 39 | Attribute_T attribute_; 40 | }; 41 | } // detail 42 | } // Rice 43 | 44 | #endif // Rice__detail__Native_Attribute_Set__hpp_ 45 | -------------------------------------------------------------------------------- /rice/detail/NativeCallbackFFI.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Native_Callback_Ffi_hpp_ 2 | #define Rice__detail__Native_Callback_Ffi_hpp_ 3 | 4 | #ifdef HAVE_LIBFFI 5 | 6 | #include 7 | 8 | namespace Rice::detail 9 | { 10 | template 11 | class NativeCallbackFFI; 12 | 13 | template 14 | class NativeCallbackFFI 15 | { 16 | public: 17 | using Callback_T = Return_T(Arg_Ts...); 18 | using Tuple_T = std::tuple; 19 | static void ffiCallback(ffi_cif* cif, void* ret, void* args[], void* instance); 20 | static VALUE finalizerCallback(VALUE yielded_arg, VALUE callback_arg, int argc, const VALUE* argv, VALUE blockarg); 21 | static void setMethodInfo(MethodInfo* methodInfo); 22 | 23 | public: 24 | NativeCallbackFFI(VALUE proc); 25 | ~NativeCallbackFFI(); 26 | NativeCallbackFFI(const NativeCallbackFFI&) = delete; 27 | NativeCallbackFFI(NativeCallbackFFI&&) = delete; 28 | void operator=(const NativeCallbackFFI&) = delete; 29 | void operator=(NativeCallbackFFI&&) = delete; 30 | 31 | Return_T operator()(Arg_Ts...args); 32 | Callback_T* callback(); 33 | 34 | private: 35 | template 36 | static ffi_type* ffiType(); 37 | 38 | template 39 | static Tuple_T convertArgsToTuple(void* args[], std::index_sequence& indices); 40 | 41 | static inline std::array args_ = { ffiType()... }; 42 | static inline ffi_cif cif_; 43 | static inline ffi_closure* closure_ = nullptr; 44 | static inline Callback_T* callback_ = nullptr; 45 | static inline std::unique_ptr methodInfo_ = std::make_unique(); 46 | 47 | template 48 | Return_T callRuby(std::index_sequence& indices, Arg_Ts...args); 49 | private: 50 | VALUE proc_; 51 | }; 52 | } 53 | #endif // HAVE_LIBFFI 54 | 55 | #endif // Rice__detail__Native_Callback_Ffi_hpp_ 56 | -------------------------------------------------------------------------------- /rice/detail/NativeCallbackSimple.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Native_Callback_Simple_hpp_ 2 | #define Rice__detail__Native_Callback_Simple_hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | template 7 | class NativeCallbackSimple; 8 | 9 | template 10 | class NativeCallbackSimple 11 | { 12 | public: 13 | static Return_T callback(Arg_Ts...args); 14 | static inline VALUE proc = Qnil; 15 | static void setMethodInfo(MethodInfo* methodInfo); 16 | 17 | public: 18 | NativeCallbackSimple() = delete; 19 | NativeCallbackSimple(const NativeCallbackSimple&) = delete; 20 | NativeCallbackSimple(NativeCallbackSimple&&) = delete; 21 | void operator=(const NativeCallbackSimple&) = delete; 22 | void operator=(NativeCallbackSimple&&) = delete; 23 | 24 | private: 25 | template 26 | static Return_T callRuby(std::index_sequence& indices, Arg_Ts...args); 27 | static inline std::unique_ptr methodInfo_ = std::make_unique(); 28 | }; 29 | } 30 | #endif // Rice__detail__Native_Callback_Simple_hpp_ 31 | -------------------------------------------------------------------------------- /rice/detail/NativeCallbackSimple.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | template 4 | void NativeCallbackSimple::setMethodInfo(MethodInfo* methodInfo) 5 | { 6 | methodInfo_.reset(methodInfo); 7 | } 8 | 9 | template 10 | template 11 | Return_T NativeCallbackSimple::callRuby(std::index_sequence& indices, Arg_Ts...args) 12 | { 13 | static Identifier id("call"); 14 | std::array values = { detail::To_Ruby>(methodInfo_->arg(I)).convert(args)... }; 15 | VALUE result = detail::protect(rb_funcallv, proc, id.id(), (int)sizeof...(Arg_Ts), values.data()); 16 | if constexpr (!std::is_void_v) 17 | { 18 | static From_Ruby fromRuby(dynamic_cast(&methodInfo_->returnInfo)); 19 | return fromRuby.convert(result); 20 | } 21 | } 22 | 23 | template 24 | Return_T NativeCallbackSimple::callback(Arg_Ts...args) 25 | { 26 | auto indices = std::make_index_sequence{}; 27 | return NativeCallbackSimple::callRuby(indices, args...); 28 | } 29 | } -------------------------------------------------------------------------------- /rice/detail/NativeIterator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__NativeIterator__hpp_ 2 | #define Rice__NativeIterator__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | template 7 | class NativeIterator: Native 8 | { 9 | public: 10 | using NativeIterator_T = NativeIterator; 11 | using Iterator_T = typename function_traits::return_type; 12 | using Value_T = typename std::iterator_traits::value_type; 13 | using Reference_T = typename std::iterator_traits::reference; 14 | using Difference_T = typename std::iterator_traits::difference_type; 15 | using To_Ruby_T = remove_cv_recursive_t; 16 | 17 | public: 18 | // Register function with Ruby 19 | void static define(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end); 20 | 21 | public: 22 | // Disallow creating/copying/moving 23 | NativeIterator() = delete; 24 | NativeIterator(const NativeIterator_T&) = delete; 25 | NativeIterator(NativeIterator_T&&) = delete; 26 | void operator=(const NativeIterator_T&) = delete; 27 | void operator=(NativeIterator_T&&) = delete; 28 | 29 | Resolved matches(size_t argc, const VALUE* argv, VALUE self) override; 30 | VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override; 31 | std::string toString() override; 32 | 33 | protected: 34 | NativeIterator(VALUE klass, std::string method_name, Iterator_Func_T begin, Iterator_Func_T end); 35 | 36 | private: 37 | VALUE createRubyEnumerator(VALUE self); 38 | 39 | private: 40 | VALUE klass_; 41 | std::string method_name_; 42 | Iterator_Func_T begin_; 43 | Iterator_Func_T end_; 44 | }; 45 | } 46 | 47 | #endif // Rice__NativeIterator__hpp_ 48 | -------------------------------------------------------------------------------- /rice/detail/NativeRegistry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__NativeRegistry__hpp 2 | #define Rice__detail__NativeRegistry__hpp 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* The Native Registry tracks C++ instance that are used to invoke C++ methods for Ruby. 9 | These objects include instances of the NativeFunction, NativeIterator, NativeAttributeGet 10 | and NativeAttributeSet Each instance is specialized to call a specific C++ function, method 11 | or attribute that is exposed to Ruby. 12 | 13 | The registry stores these C++ instances using a map of vectors. The map is keyed on the 14 | the Ruby class (VALUE) and method id (ID). The value is a vector of Native pointers stored 15 | in a std::unique_ptr. Thus the registry takes ownership of the pointers when calling 16 | code adds them to the registry. The value is a vector to support C++ method overloading. 17 | 18 | Note - when an existing Ruby class is redefined using rb_define_class, its VALUE stays the same 19 | but all its methods and fields are reset. Thus any call to rb_define_class must be followed 20 | by calling the reset method on the registry. Although redefinition shouldn't happen in 21 | production code it happens in many places in the unit tests. */ 22 | 23 | namespace Rice::detail 24 | { 25 | class NativeRegistry 26 | { 27 | public: 28 | void add(VALUE klass, ID methodId, std::unique_ptr& native); 29 | void reset(VALUE klass); 30 | const std::vector>& lookup(VALUE klass, ID methodId); 31 | 32 | private: 33 | // Key - Ruby klass/method 34 | // Value - Vector of Native pointers owned by the registry (thus wrapped in std::unique_ptr) 35 | std::map, std::vector>> natives_ = {}; 36 | }; 37 | } 38 | 39 | #endif // Rice__detail__NativeRegistry__hpp 40 | -------------------------------------------------------------------------------- /rice/detail/NativeRegistry.ipp: -------------------------------------------------------------------------------- 1 | 2 | namespace Rice::detail 3 | { 4 | inline void NativeRegistry::add(VALUE klass, ID methodId, std::unique_ptr& native) 5 | { 6 | if (rb_type(klass) == T_ICLASS) 7 | { 8 | klass = detail::protect(rb_class_of, klass); 9 | } 10 | 11 | // Create the key 12 | std::pair key = std::make_pair(klass, methodId); 13 | 14 | // Lookup items for method 15 | std::vector>& natives = this->natives_[key]; 16 | 17 | natives.push_back(std::move(native)); 18 | } 19 | 20 | inline void NativeRegistry::reset(VALUE klass) 21 | { 22 | for (auto iter = this->natives_.begin(); iter != this->natives_.end();) 23 | { 24 | // Iter points to a std::pair, std::vector 25 | if (iter->first.first == klass) 26 | { 27 | iter = this->natives_.erase(iter); 28 | } 29 | else 30 | { 31 | ++iter; 32 | } 33 | } 34 | } 35 | 36 | inline const std::vector>& NativeRegistry::lookup(VALUE klass, ID methodId) 37 | { 38 | if (rb_type(klass) == T_ICLASS) 39 | { 40 | klass = detail::protect(rb_class_of, klass); 41 | } 42 | 43 | // Create the key 44 | std::pair key = std::make_pair(klass, methodId); 45 | 46 | // Lookup items for method 47 | return this->natives_[key]; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rice/detail/Proc.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Proc__hpp_ 2 | #define Rice__Proc__hpp_ 3 | 4 | #endif // Rice__Proc__hpp_ 5 | -------------------------------------------------------------------------------- /rice/detail/Proc.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | // Note Return_T(Arg_Ts...) is intentional versus Return_T(*)(Arg_Ts...). That is 4 | // because the Type machinery strips all pointers/references/const/val etc to avoid 5 | // having an explosion of Type definitions 6 | template 7 | struct Type 8 | { 9 | static bool verify() 10 | { 11 | return true; 12 | } 13 | }; 14 | 15 | // Wraps a C++ function as a Ruby proc 16 | template 17 | class To_Ruby 18 | { 19 | public: 20 | using Proc_T = Return_T(*)(Arg_Ts...); 21 | 22 | VALUE convert(Proc_T proc) 23 | { 24 | using NativeFunction_T = NativeFunction; 25 | auto native = new NativeFunction_T(proc); 26 | VALUE result = rb_proc_new(NativeFunction_T::procEntry, (VALUE)native); 27 | 28 | // Tie the lifetime of the NativeCallback C++ instance to the lifetime of the Ruby proc object 29 | VALUE finalizer = rb_proc_new(NativeFunction_T::finalizerCallback, (VALUE)native); 30 | rb_define_finalizer(result, finalizer); 31 | 32 | return result; 33 | } 34 | }; 35 | 36 | // Makes a Ruby proc callable as C callback 37 | template 38 | class From_Ruby 39 | { 40 | public: 41 | using Callback_T = Return_T(*)(Arg_Ts...); 42 | 43 | From_Ruby() = default; 44 | 45 | explicit From_Ruby(Arg* arg) : arg_(arg) 46 | { 47 | } 48 | 49 | Convertible is_convertible(VALUE value) 50 | { 51 | if (protect(rb_obj_is_proc, value) == Qtrue || protect(rb_proc_lambda_p, value)) 52 | { 53 | return Convertible::Exact; 54 | } 55 | else 56 | { 57 | return Convertible::None; 58 | } 59 | } 60 | 61 | #ifdef HAVE_LIBFFI 62 | Callback_T convert(VALUE value) 63 | { 64 | using NativeCallback_T = NativeCallbackFFI; 65 | NativeCallback_T* nativeCallback = new NativeCallback_T(value); 66 | 67 | // Tie the lifetime of the NativeCallback C++ instance to the lifetime of the Ruby proc object 68 | VALUE finalizer = rb_proc_new(NativeCallback_T::finalizerCallback, (VALUE)nativeCallback); 69 | rb_define_finalizer(value, finalizer); 70 | 71 | return nativeCallback->callback(); 72 | } 73 | #else 74 | Callback_T convert(VALUE value) 75 | { 76 | using NativeCallback_T = NativeCallbackSimple; 77 | NativeCallback_T::proc = value; 78 | return &NativeCallback_T::callback; 79 | } 80 | #endif 81 | 82 | private: 83 | Arg* arg_ = nullptr; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /rice/detail/Registries.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Registries__hpp_ 2 | #define Rice__Registries__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | class Registries 7 | { 8 | public: 9 | static Registries instance; 10 | 11 | public: 12 | HandlerRegistry handlers; 13 | InstanceRegistry instances; 14 | NativeRegistry natives; 15 | TypeRegistry types; 16 | }; 17 | } 18 | 19 | #endif // Rice__Registries__hpp_ 20 | -------------------------------------------------------------------------------- /rice/detail/Registries.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | //Initialize static variables here. 4 | inline Registries Registries::instance; 5 | } 6 | -------------------------------------------------------------------------------- /rice/detail/RubyFunction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__ruby_function__hpp_ 2 | #define Rice__detail__ruby_function__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | /* This is functor class that wraps calls to a Ruby C API method. It is needed because 7 | rb_protect only supports calling methods that take one argument. Thus 8 | we invoke rb_protect telling it to invoke Ruby_Function::call with an 9 | instance of a Ruby_Function. That instance then in turn calls the original 10 | Ruby method passing along its required arguments. */ 11 | 12 | template 13 | class RubyFunction 14 | { 15 | public: 16 | using Return_T = typename function_traits::return_type; 17 | 18 | public: 19 | RubyFunction(Function_T func, const Arg_Ts&... args); 20 | Return_T operator()(); 21 | 22 | private: 23 | Function_T func_; 24 | std::tuple args_; 25 | }; 26 | 27 | template 28 | auto protect(Function_T func, Arg_Ts...args); 29 | } 30 | 31 | #endif // Rice__detail__ruby_function__hpp_ -------------------------------------------------------------------------------- /rice/detail/RubyFunction.ipp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | namespace Rice::detail 5 | { 6 | template 7 | inline RubyFunction::RubyFunction(Function_T func, const Arg_Ts&... args) 8 | : func_(func), args_(std::forward_as_tuple(args...)) 9 | { 10 | } 11 | 12 | template 13 | inline typename RubyFunction::Return_T RubyFunction::operator()() 14 | { 15 | // Setup a thread local variable to capture the result of the Ruby function call. 16 | // We use thread_local because the lambda has to be captureless so it can 17 | // be converted to a function pointer callable by C. 18 | // The thread local variable avoids having to cast the result to VALUE and then 19 | // back again to Return_T. The problem with that is the translation is not lossless 20 | // in some cases - for example a double with value of -1.0 does not roundrip. 21 | // 22 | thread_local std::any result; 23 | 24 | // Callback that will invoke the Ruby function 25 | using Functor_T = RubyFunction; 26 | auto callback = [](VALUE value) -> VALUE 27 | { 28 | Functor_T* functor = (Functor_T*)value; 29 | 30 | if constexpr (std::is_same_v) 31 | { 32 | std::apply(functor->func_, functor->args_); 33 | } 34 | else 35 | { 36 | result = std::apply(functor->func_, functor->args_); 37 | } 38 | 39 | return Qnil; 40 | }; 41 | 42 | // Now call rb_protect which will invoke the callback lambda above 43 | int state = (int)JumpException::RUBY_TAG_NONE; 44 | rb_protect(callback, (VALUE)this, &state); 45 | 46 | // Did anything go wrong? 47 | if (state == JumpException::RUBY_TAG_NONE) 48 | { 49 | if constexpr (!std::is_same_v) 50 | { 51 | return std::any_cast(result); 52 | } 53 | } 54 | else 55 | { 56 | VALUE err = rb_errinfo(); 57 | if (state == JumpException::RUBY_TAG_RAISE && RB_TEST(err)) 58 | { 59 | rb_set_errinfo(Qnil); 60 | throw Rice::Exception(err); 61 | } 62 | else 63 | { 64 | throw Rice::JumpException((Rice::JumpException::ruby_tag_type)state); 65 | } 66 | } 67 | } 68 | 69 | // Create a functor for calling a Ruby function and define some aliases for readability. 70 | template 71 | auto protect(Function_T func, Arg_Ts...args) 72 | { 73 | auto rubyFunction = RubyFunction(func, std::forward(args)...); 74 | return rubyFunction(); 75 | } 76 | } -------------------------------------------------------------------------------- /rice/detail/RubyType.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__ruby__type__hpp_ 2 | #define Rice__detail__ruby__type__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | template 9 | class RubyType 10 | { 11 | }; 12 | 13 | void define_ruby_types(); 14 | } 15 | 16 | #endif // Rice__detail__ruby__type__hpp_ 17 | -------------------------------------------------------------------------------- /rice/detail/Type.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Type__hpp_ 2 | #define Rice__Type__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | template 9 | struct Type 10 | { 11 | static bool verify(); 12 | }; 13 | 14 | // Return the name of a type 15 | std::string typeName(const std::type_info& typeInfo); 16 | std::string typeName(const std::type_index& typeIndex); 17 | std::string cppClassName(const std::string& typeInfoName); 18 | std::string rubyClassName(const std::string& typeInfoName); 19 | std::string findGroup(std::string& string, size_t start = 0); 20 | void replaceGroup(std::string& string, std::regex regex, std::string replacement); 21 | void replaceAll(std::string& string, std::regex regex, std::string replacement); 22 | 23 | template 24 | void verifyType(); 25 | 26 | template 27 | void verifyTypes(); 28 | } 29 | 30 | #endif // Rice__Type__hpp_ 31 | -------------------------------------------------------------------------------- /rice/detail/TypeRegistry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__TypeRegistry__hpp_ 2 | #define Rice__TypeRegistry__hpp_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | /* The type registry keeps track of all C++ types wrapped by Rice. When a native function returns 11 | an instance of a class/struct we look up its type to verity that it has been registered. 12 | 13 | We have to do this to support C++ inheritance. If a C++ function returns a pointer/reference 14 | to an Abstract class, the actual returned object will be a Child class. However, all we know 15 | from the C++ method signature is that it is an Absract class - thus the need for a registry.*/ 16 | 17 | namespace Rice::detail 18 | { 19 | class TypeRegistry 20 | { 21 | public: 22 | template 23 | void add(VALUE klass, rb_data_type_t* rbType); 24 | 25 | template 26 | void remove(); 27 | 28 | template 29 | bool isDefined(); 30 | 31 | template 32 | std::pair getType(); 33 | 34 | template 35 | bool verify(); 36 | 37 | template 38 | std::pair figureType(const T& object); 39 | 40 | // Validate types and throw if any types are unverified 41 | void validateTypes(); 42 | 43 | // Clear unverified types. This is mostly for unit tests 44 | void clearUnverifiedTypes(); 45 | 46 | private: 47 | std::optional> lookup(const std::type_info& typeInfo); 48 | void raiseUnverifiedType(const std::string& typeName); 49 | 50 | std::unordered_map> registry_{}; 51 | std::set unverified_{}; 52 | }; 53 | } 54 | 55 | #endif // Rice__TypeRegistry__hpp_ 56 | -------------------------------------------------------------------------------- /rice/detail/Wrapper.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__Wrapper__hpp_ 2 | #define Rice__detail__Wrapper__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | class WrapperBase 7 | { 8 | public: 9 | WrapperBase() = default; 10 | virtual ~WrapperBase() = default; 11 | virtual void* get() = 0; 12 | bool isConst(); 13 | 14 | void ruby_mark(); 15 | void addKeepAlive(VALUE value); 16 | void setOwner(bool value); 17 | 18 | protected: 19 | bool isOwner_ = false; 20 | bool isConst_ = false; 21 | 22 | private: 23 | // We use a vector for speed and memory locality versus a set which does 24 | // not scale well when getting to tens of thousands of objects (not expecting 25 | // that to happen...but just in case) 26 | std::vector keepAlive_; 27 | }; 28 | 29 | template 30 | class Wrapper : public WrapperBase 31 | { 32 | public: 33 | Wrapper(T& data); 34 | Wrapper(T&& data); 35 | ~Wrapper(); 36 | void* get() override; 37 | 38 | private: 39 | T data_; 40 | }; 41 | 42 | template 43 | class Wrapper : public WrapperBase 44 | { 45 | public: 46 | Wrapper(T& data); 47 | ~Wrapper(); 48 | void* get() override; 49 | 50 | private: 51 | T& data_; 52 | }; 53 | 54 | template 55 | class Wrapper : public WrapperBase 56 | { 57 | public: 58 | Wrapper(T* data, bool isOwner); 59 | ~Wrapper(); 60 | void* get() override; 61 | 62 | private: 63 | T* data_ = nullptr; 64 | }; 65 | 66 | // ---- Helper Functions --------- 67 | template 68 | void wrapConstructed(VALUE value, rb_data_type_t* rb_data_type, T* data, bool isOwner); 69 | 70 | template 71 | VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T& data, bool isOwner); 72 | 73 | template 74 | VALUE wrap(VALUE klass, rb_data_type_t* rb_data_type, T* data, bool isOwner); 75 | 76 | template 77 | T* unwrap(VALUE value, rb_data_type_t* rb_data_type, bool takeOwnership); 78 | 79 | template 80 | Wrapper_T* getWrapper(VALUE value, rb_data_type_t* rb_data_type); 81 | 82 | WrapperBase* getWrapper(VALUE value); 83 | } 84 | #endif // Rice__detail__Wrapper__hpp_ 85 | -------------------------------------------------------------------------------- /rice/detail/default_allocation_func.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__default_allocation_func__hpp_ 2 | #define Rice__detail__default_allocation_func__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | //! A default implementation of an allocate_func. This function does no 7 | //! actual allocation; the initialize_func can later do the real 8 | //! allocation with: DATA_PTR(self) = new Type(arg1, arg2, ...) 9 | template 10 | VALUE default_allocation_func(VALUE klass); 11 | } 12 | #endif // Rice__detail__default_allocation_func__hpp_ -------------------------------------------------------------------------------- /rice/detail/default_allocation_func.ipp: -------------------------------------------------------------------------------- 1 | namespace Rice::detail 2 | { 3 | template 4 | VALUE default_allocation_func(VALUE klass) 5 | { 6 | // Create a new Ruby object but since we do not yet have a C++ object 7 | // just pass a nullptr. It will be set via the Constructor call 8 | return TypedData_Wrap_Struct(klass, Data_Type::ruby_data_type(), nullptr); 9 | } 10 | } -------------------------------------------------------------------------------- /rice/detail/from_ruby.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__from_ruby__hpp_ 2 | #define Rice__detail__from_ruby__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | //! Convert a Ruby object to C++. 7 | /*! If the Ruby object can be converted to an immediate value, returns a 8 | * copy of the Ruby object. If the Ruby object is holding a C++ 9 | * object and the type specified is a pointer to that type, returns a 10 | * pointer to that object. 11 | * 12 | * Conversions from ruby to a pointer type are automatically generated 13 | * when a type is bound using Data_Type. If no conversion exists an 14 | * exception is thrown. 15 | * 16 | * \param T the C++ type to which to convert. 17 | * \param x the Ruby object to convert. 18 | * \return a C++ representation of the Ruby object. 19 | * 20 | * Example: 21 | * \code 22 | * Object x = INT2NUM(42); 23 | * std::cout << From_Ruby::convert(x); 24 | * 25 | * Data_Object foo(new Foo); 26 | * std::cout << *From_Ruby(foo) << std::endl; 27 | * \endcode 28 | */ 29 | 30 | template 31 | class From_Ruby; 32 | 33 | enum class Convertible: uint8_t 34 | { 35 | None = 0b0000, 36 | Narrow = 0b0001, 37 | Cast = 0b0011, 38 | Const = 0b0111, 39 | Exact = 0b1111 40 | }; 41 | } 42 | 43 | #endif // Rice__detail__From_Ruby2__hpp_ 44 | -------------------------------------------------------------------------------- /rice/detail/ruby.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__ruby__hpp_ 2 | #define Rice__detail__ruby__hpp_ 3 | 4 | /*! \file 5 | * \brief Hacks for addressing incompatibilities between various Ruby 6 | * versions. 7 | */ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | // ruby.h has a few defines that conflict with Visual Studio's STL 15 | #if defined(_MSC_VER) 16 | #undef write 17 | #undef read 18 | #undef bind 19 | #endif 20 | 21 | // And some c library conflicts 22 | #undef isnan 23 | #undef snprintf 24 | #undef vsnprintf 25 | 26 | //! A function that takes a VALUE as a parameter and returns a VALUE. 27 | // TODO: Casting from a C++ function to an extern "C" function won't 28 | // work on all platforms. I'm not sure what to do about this. 29 | extern "C" typedef VALUE (*RUBY_VALUE_FUNC)(VALUE); 30 | 31 | // Fix Ruby RUBY_METHOD_FUNC from macro to typedef 32 | #if defined(RUBY_METHOD_FUNC) 33 | # undef RUBY_METHOD_FUNC 34 | extern "C" typedef VALUE (*RUBY_METHOD_FUNC)(ANYARGS); 35 | #endif 36 | 37 | // This is a terrible hack for Ruby 3.1 and maybe earlier to avoid crashes when test_Attribute unit cases 38 | // are run. If ruby_options is called to initialize the interpeter (previously it was not), when 39 | // the attribute unit tests intentionally cause exceptions to happen, the exception is correctly processed. 40 | // However any calls back to Ruby, for example to get the exception message, crash because the ruby 41 | // execution context tag has been set to null. This does not happen in newer versions of Ruby. It is 42 | // unknown if this happens in real life or just the test caes. 43 | // Should be removed when Rice no longer supports Ruby 3.1 44 | #if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR < 2 45 | constexpr bool oldRuby = true; 46 | #elif RUBY_API_VERSION_MAJOR < 3 47 | constexpr bool oldRuby = true; 48 | #else 49 | constexpr bool oldRuby = false; 50 | #endif 51 | 52 | 53 | 54 | #endif // Rice__detail__ruby__hpp_ 55 | 56 | -------------------------------------------------------------------------------- /rice/detail/to_ruby.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__to_ruby__hpp_ 2 | #define Rice__detail__to_ruby__hpp_ 3 | 4 | namespace Rice 5 | { 6 | namespace detail 7 | { 8 | //! Convert a C++ object to Ruby. 9 | /*! If x is a pointer, wraps the pointee as a Ruby object. If x is an 10 | * Object, returns x. 11 | * 12 | * If no conversion exists a compile-time error is generated. 13 | * 14 | * \param x the object to convert. 15 | * \return a Ruby representation of the C++ object. 16 | * 17 | * Example: 18 | * \code 19 | * rb_p(to_ruby(42)); 20 | * 21 | * Foo * p_foo = new Foo(); 22 | * rb_p(to_ruby(p_foo)); 23 | * \endcode 24 | */ 25 | template 26 | class To_Ruby; 27 | 28 | // Helper template function that let's users avoid having to specify the template type - its deduced 29 | template 30 | VALUE to_ruby(T&& x) 31 | { 32 | using Unqualified_T = remove_cv_recursive_t; 33 | return To_Ruby().convert(std::forward(x)); 34 | } 35 | 36 | // Helper template function that let's users avoid having to specify the template type - its deduced 37 | template 38 | VALUE to_ruby(T* x) 39 | { 40 | using Unqualified_T = remove_cv_recursive_t; 41 | return To_Ruby().convert(x); 42 | } 43 | } // detail 44 | } // Rice 45 | 46 | #endif // Rice__detail__to_ruby__hpp_ 47 | -------------------------------------------------------------------------------- /rice/forward_declares.ipp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__Forward_Declares__ipp_ 2 | #define Rice__Forward_Declares__ipp_ 3 | 4 | namespace Rice 5 | { 6 | // These methods cannot be defined where they are declared due to circular dependencies 7 | inline Class Object::class_of() const 8 | { 9 | return detail::protect(rb_class_of, value_); 10 | } 11 | 12 | inline String Object::to_s() const 13 | { 14 | return call("to_s"); 15 | } 16 | 17 | inline String Object::class_name() const 18 | { 19 | return detail::protect(rb_obj_classname, this->value()); 20 | } 21 | 22 | inline String Object::inspect() const 23 | { 24 | return call("inspect"); 25 | } 26 | 27 | inline Object Object::instance_eval(String const& s) 28 | { 29 | const VALUE argv[] = { s.value() }; 30 | return detail::protect(rb_obj_instance_eval, 1, &argv[0], this->value()); 31 | } 32 | 33 | inline Object Object::vcall(Identifier id, Array args) 34 | { 35 | std::vector a(args.size()); 36 | 37 | Array::const_iterator it = args.begin(); 38 | Array::const_iterator end = args.end(); 39 | 40 | for (int i = 0; it != end; i++, ++it) 41 | { 42 | a[i] = it->value(); 43 | } 44 | 45 | return detail::protect(rb_funcall3, this->value(), id.id(), (int)args.size(), (const VALUE*)a.data()); 46 | } 47 | 48 | inline std::ostream& operator<<(std::ostream& out, Object const& obj) 49 | { 50 | String s(obj.to_s()); 51 | out << s.c_str(); 52 | return out; 53 | } 54 | 55 | inline Identifier::Identifier(Symbol const& symbol) : id_(SYM2ID(symbol.value())) 56 | { 57 | } 58 | 59 | inline String Module::name() const 60 | { 61 | VALUE name = detail::protect(rb_mod_name, this->value()); 62 | if (name == Qnil) 63 | { 64 | return String(""); 65 | } 66 | else 67 | { 68 | return name; 69 | } 70 | } 71 | 72 | inline Array Module::ancestors() const 73 | { 74 | return detail::protect(rb_mod_ancestors, this->value()); 75 | } 76 | 77 | inline Class Module::singleton_class() const 78 | { 79 | return CLASS_OF(value()); 80 | } 81 | 82 | inline Object Module::module_eval(String const& s) 83 | { 84 | const VALUE argv[] = { s.value() }; 85 | return detail::protect(rb_mod_module_eval, 1, &argv[0], this->value()); 86 | } 87 | } 88 | #endif // Rice__Forward_Declares__ipp_ 89 | -------------------------------------------------------------------------------- /rice/global_function.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__global_function__hpp_ 2 | #define Rice__global_function__hpp_ 3 | 4 | namespace Rice 5 | { 6 | //! Define an global function 7 | /*! The method's implementation can be any function or static member 8 | * function. A wrapper will be generated which will convert the arguments 9 | * from ruby types to C++ types before calling the function. The return 10 | * value will be converted back to ruby. 11 | * \param name the name of the method 12 | * \param func the implementation of the function, either a function 13 | * pointer or a member function pointer. 14 | * \param args a list of Arg instance used to define default parameters (optional) 15 | * \return *this 16 | */ 17 | template 18 | void define_global_function(char const * name, Function_T&& func, Arg_Ts const& ...args); 19 | } // Rice 20 | 21 | #endif // Rice__global_function__hpp_ 22 | -------------------------------------------------------------------------------- /rice/global_function.ipp: -------------------------------------------------------------------------------- 1 | 2 | template 3 | void Rice::define_global_function(char const * name, Function_T&& func, Arg_Ts const& ...args) 4 | { 5 | Module(rb_mKernel).define_module_function(name, std::forward(func), args...); 6 | } -------------------------------------------------------------------------------- /rice/libc/file.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__libc__file__hpp_ 2 | #define Rice__libc__file__hpp_ 3 | 4 | namespace Rice::libc 5 | { 6 | extern Class rb_cLibcFile; 7 | } 8 | 9 | #include "file.ipp" 10 | 11 | #endif // Rice__libc__file__hpp_ -------------------------------------------------------------------------------- /rice/libc/file.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Libraries sometime inherit custom exception objects from std::exception, 4 | // so define it for Ruby if necessary 5 | namespace Rice::libc 6 | { 7 | inline Class rb_cLibcFile; 8 | 9 | inline void define_libc_file() 10 | { 11 | Module rb_mRice = define_module("Rice"); 12 | Module rb_mLibc = define_module_under(rb_mRice, "libc"); 13 | rb_cLibcFile = define_class_under(rb_mLibc, "File"); 14 | } 15 | } 16 | 17 | namespace Rice::detail 18 | { 19 | template<> 20 | struct Type 21 | { 22 | static bool verify() 23 | { 24 | if (!Data_Type::is_defined()) 25 | { 26 | libc::define_libc_file(); 27 | } 28 | 29 | return true; 30 | } 31 | }; 32 | } 33 | -------------------------------------------------------------------------------- /rice/ruby_mark.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__ruby_mark__hpp_ 2 | #define Rice__ruby_mark__hpp_ 3 | 4 | //! Default function to call to mark a data object. 5 | /*! This function can be specialized for a particular type to override 6 | * the default behavior (which is to not mark any additional objects). 7 | */ 8 | namespace Rice 9 | { 10 | template 11 | void ruby_mark(T* data) 12 | { 13 | } 14 | } 15 | #endif // Rice__ruby_mark__hpp_ 16 | 17 | -------------------------------------------------------------------------------- /rice/stl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__hpp_ 2 | #define Rice__stl__hpp_ 3 | 4 | #include "stl/exception.hpp" 5 | #include "stl/exception_ptr.hpp" 6 | #include "stl/string.hpp" 7 | #include "stl/string_view.hpp" 8 | #include "stl/complex.hpp" 9 | #include "stl/optional.hpp" 10 | #include "stl/reference_wrapper.hpp" 11 | #include "stl/pair.hpp" 12 | #include "stl/map.hpp" 13 | #include "stl/monostate.hpp" 14 | #include "stl/multimap.hpp" 15 | #include "stl/set.hpp" 16 | #include "stl/shared_ptr.hpp" 17 | #include "stl/tuple.hpp" 18 | #include "stl/type_index.hpp" 19 | #include "stl/type_info.hpp" 20 | #include "stl/variant.hpp" 21 | #include "stl/unique_ptr.hpp" 22 | #include "stl/unordered_map.hpp" 23 | #include "stl/vector.hpp" 24 | 25 | #endif // Rice__stl__hpp_ 26 | -------------------------------------------------------------------------------- /rice/stl/complex.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__complex__hpp_ 2 | #define Rice__stl__complex__hpp_ 3 | 4 | #include "complex.ipp" 5 | 6 | #endif // Rice__stl__complex__hpp_ -------------------------------------------------------------------------------- /rice/stl/complex.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template 6 | struct Type> 7 | { 8 | static bool verify() 9 | { 10 | return true; 11 | } 12 | }; 13 | 14 | template 15 | class To_Ruby> 16 | { 17 | public: 18 | VALUE convert(const std::complex& data) 19 | { 20 | std::vector args(2); 21 | args[0] = To_Ruby().convert(data.real()); 22 | args[1] = To_Ruby().convert(data.imag()); 23 | return protect(rb_funcallv, rb_mKernel, rb_intern("Complex"), (int)args.size(), (const VALUE*)args.data()); 24 | } 25 | }; 26 | 27 | template 28 | class To_Ruby&> 29 | { 30 | public: 31 | VALUE convert(const std::complex& data) 32 | { 33 | std::vector args(2); 34 | args[0] = To_Ruby().convert(data.real()); 35 | args[1] = To_Ruby().convert(data.imag()); 36 | return protect(rb_funcallv, rb_mKernel, rb_intern("Complex"), (int)args.size(), (const VALUE*)args.data()); 37 | } 38 | }; 39 | 40 | template 41 | class From_Ruby> 42 | { 43 | public: 44 | Convertible is_convertible(VALUE value) 45 | { 46 | switch (rb_type(value)) 47 | { 48 | case RUBY_T_COMPLEX: 49 | return Convertible::Exact; 50 | break; 51 | default: 52 | return Convertible::None; 53 | } 54 | } 55 | 56 | std::complex convert(VALUE value) 57 | { 58 | VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr); 59 | VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr); 60 | 61 | return std::complex(From_Ruby().convert(real), From_Ruby().convert(imaginary)); 62 | } 63 | }; 64 | 65 | template 66 | class From_Ruby&> 67 | { 68 | public: 69 | Convertible is_convertible(VALUE value) 70 | { 71 | switch (rb_type(value)) 72 | { 73 | case RUBY_T_COMPLEX: 74 | return Convertible::Exact; 75 | break; 76 | default: 77 | return Convertible::None; 78 | } 79 | } 80 | 81 | std::complex& convert(VALUE value) 82 | { 83 | VALUE real = protect(rb_funcallv, value, rb_intern("real"), 0, (const VALUE*)nullptr); 84 | VALUE imaginary = protect(rb_funcallv, value, rb_intern("imaginary"), 0, (const VALUE*)nullptr); 85 | this->converted_ = std::complex(From_Ruby().convert(real), From_Ruby().convert(imaginary)); 86 | 87 | return this->converted_; 88 | } 89 | 90 | private: 91 | std::complex converted_; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /rice/stl/exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__exception__hpp_ 2 | #define Rice__stl__exception__hpp_ 3 | 4 | namespace Rice::stl 5 | { 6 | extern Class rb_cStlException; 7 | } 8 | 9 | #include "exception.ipp" 10 | 11 | #endif // Rice__stl__exception__hpp_ -------------------------------------------------------------------------------- /rice/stl/exception.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Libraries sometime inherit custom exception objects from std::exception, 4 | // so define it for Ruby if necessary 5 | namespace Rice::stl 6 | { 7 | inline Class rb_cStlException; 8 | 9 | inline void define_stl_exception() 10 | { 11 | Module rb_mStd = define_module("Std"); 12 | rb_cStlException = define_class_under(rb_mStd, "Exception", rb_eStandardError). 13 | define_constructor(Constructor()). 14 | define_method("message", &std::exception::what); 15 | } 16 | } 17 | 18 | namespace Rice::detail 19 | { 20 | template<> 21 | struct Type 22 | { 23 | static bool verify() 24 | { 25 | Rice::stl::define_stl_exception(); 26 | return true; 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /rice/stl/exception_ptr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__exception_ptr__hpp_ 2 | #define Rice__stl__exception_ptr__hpp_ 3 | 4 | #include "exception_ptr.ipp" 5 | 6 | #endif // Rice__stl__exception_ptr__hpp_ -------------------------------------------------------------------------------- /rice/stl/exception_ptr.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::stl 4 | { 5 | inline Data_Type define_exception_ptr() 6 | { 7 | Module rb_mStd = define_module("Std"); 8 | return define_class_under(rb_mStd, "ExceptionPtr"); 9 | } 10 | } 11 | 12 | namespace Rice::detail 13 | { 14 | template<> 15 | struct Type 16 | { 17 | static bool verify() 18 | { 19 | if (!Data_Type::is_defined()) 20 | { 21 | stl::define_exception_ptr(); 22 | } 23 | 24 | return true; 25 | } 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /rice/stl/map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__map__hpp_ 2 | #define Rice__stl__map__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | Data_Type> define_map(std::string name = ""); 8 | } 9 | 10 | #include "map.ipp" 11 | 12 | #endif // Rice__stl__map__hpp_ -------------------------------------------------------------------------------- /rice/stl/monostate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__monostate__hpp_ 2 | #define Rice__stl__monostate__hpp_ 3 | 4 | #include "monostate.ipp" 5 | 6 | #endif // Rice__stl__monostate__hpp_ -------------------------------------------------------------------------------- /rice/stl/monostate.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template<> 6 | struct Type 7 | { 8 | constexpr static bool verify() 9 | { 10 | return true; 11 | } 12 | }; 13 | 14 | template<> 15 | class To_Ruby 16 | { 17 | public: 18 | VALUE convert(const std::monostate& _) 19 | { 20 | return Qnil; 21 | } 22 | }; 23 | 24 | template<> 25 | class To_Ruby 26 | { 27 | public: 28 | static VALUE convert(const std::monostate& data, bool takeOwnership = false) 29 | { 30 | return Qnil; 31 | } 32 | }; 33 | 34 | template<> 35 | class From_Ruby 36 | { 37 | public: 38 | Convertible is_convertible(VALUE value) 39 | { 40 | return value == Qnil ? Convertible::Exact : Convertible::None; 41 | } 42 | 43 | std::monostate convert(VALUE value) 44 | { 45 | if (value == Qnil) 46 | { 47 | return std::monostate(); 48 | } 49 | else 50 | { 51 | throw std::runtime_error("Can only convert nil values to std::monostate"); 52 | } 53 | } 54 | }; 55 | 56 | template<> 57 | class From_Ruby 58 | { 59 | public: 60 | Convertible is_convertible(VALUE value) 61 | { 62 | return value == Qnil ? Convertible::Exact : Convertible::None; 63 | } 64 | 65 | std::monostate& convert(VALUE value) 66 | { 67 | if (value == Qnil) 68 | { 69 | return this->converted_; 70 | } 71 | else 72 | { 73 | throw std::runtime_error("Can only convert nil values to std::monostate"); 74 | } 75 | } 76 | 77 | private: 78 | std::monostate converted_ = std::monostate(); 79 | }; 80 | } 81 | -------------------------------------------------------------------------------- /rice/stl/multimap.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__multimap__hpp_ 2 | #define Rice__stl__multimap__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice 7 | { 8 | template 9 | Data_Type> define_multimap(std::string name = ""); 10 | } 11 | 12 | #include "multimap.ipp" 13 | 14 | #endif // Rice__stl__multimap__hpp_ -------------------------------------------------------------------------------- /rice/stl/optional.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__optional__hpp_ 2 | #define Rice__stl__optional__hpp_ 3 | 4 | #include "optional.ipp" 5 | 6 | #endif // Rice__stl__optional__hpp_ -------------------------------------------------------------------------------- /rice/stl/optional.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template 6 | struct Type> 7 | { 8 | constexpr static bool verify() 9 | { 10 | return Type>::verify(); 11 | } 12 | }; 13 | 14 | template<> 15 | class To_Ruby 16 | { 17 | public: 18 | VALUE convert(const std::nullopt_t& _) 19 | { 20 | return Qnil; 21 | } 22 | }; 23 | 24 | template 25 | class To_Ruby> 26 | { 27 | public: 28 | static VALUE convert(const std::optional& data, bool takeOwnership = false) 29 | { 30 | if (data.has_value()) 31 | { 32 | return To_Ruby().convert(data.value()); 33 | } 34 | else 35 | { 36 | return Qnil; 37 | } 38 | } 39 | }; 40 | 41 | template 42 | class To_Ruby&> 43 | { 44 | public: 45 | static VALUE convert(const std::optional& data, bool takeOwnership = false) 46 | { 47 | if (data.has_value()) 48 | { 49 | return To_Ruby().convert(data.value()); 50 | } 51 | else 52 | { 53 | return Qnil; 54 | } 55 | } 56 | }; 57 | 58 | template 59 | class From_Ruby> 60 | { 61 | public: 62 | Convertible is_convertible(VALUE value) 63 | { 64 | switch (rb_type(value)) 65 | { 66 | case RUBY_T_NIL: 67 | return Convertible::Exact; 68 | break; 69 | default: 70 | return From_Ruby().is_convertible(value); 71 | } 72 | } 73 | 74 | std::optional convert(VALUE value) 75 | { 76 | if (value == Qnil) 77 | { 78 | return std::nullopt; 79 | } 80 | else 81 | { 82 | return From_Ruby().convert(value); 83 | } 84 | } 85 | }; 86 | 87 | template 88 | class From_Ruby&> 89 | { 90 | public: 91 | Convertible is_convertible(VALUE value) 92 | { 93 | switch (rb_type(value)) 94 | { 95 | case RUBY_T_NIL: 96 | return Convertible::Exact; 97 | break; 98 | default: 99 | return From_Ruby().is_convertible(value); 100 | } 101 | } 102 | 103 | std::optional& convert(VALUE value) 104 | { 105 | if (value == Qnil) 106 | { 107 | this->converted_ = std::nullopt; 108 | } 109 | else 110 | { 111 | this->converted_ = From_Ruby().convert(value); 112 | } 113 | return this->converted_; 114 | } 115 | private: 116 | std::optional converted_; 117 | }; 118 | } 119 | -------------------------------------------------------------------------------- /rice/stl/pair.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__pair__hpp_ 2 | #define Rice__stl__pair__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | Data_Type> define_pair(std::string klassName = ""); 8 | } 9 | 10 | #include "pair.ipp" 11 | 12 | #endif // Rice__stl__pair__hpp_ 13 | 14 | -------------------------------------------------------------------------------- /rice/stl/reference_wrapper.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__reference_wrapper__hpp_ 2 | #define Rice__stl__reference_wrapper__hpp_ 3 | 4 | #include "reference_wrapper.ipp" 5 | 6 | #endif // Rice__stl__reference_wrapper__hpp_ -------------------------------------------------------------------------------- /rice/stl/reference_wrapper.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template 6 | struct Type> 7 | { 8 | constexpr static bool verify() 9 | { 10 | return Type::verify(); 11 | } 12 | }; 13 | 14 | template 15 | class To_Ruby> 16 | { 17 | public: 18 | VALUE convert(const std::reference_wrapper& data, bool takeOwnership = false) 19 | { 20 | return To_Ruby().convert(data.get()); 21 | } 22 | }; 23 | 24 | template 25 | class From_Ruby> 26 | { 27 | public: 28 | Convertible is_convertible(VALUE value) 29 | { 30 | return this->converter_.is_convertible(value); 31 | } 32 | 33 | std::reference_wrapper convert(VALUE value) 34 | { 35 | return this->converter_.convert(value); 36 | } 37 | 38 | private: 39 | From_Ruby converter_; 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /rice/stl/set.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__set__hpp_ 2 | #define Rice__stl__set__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | Data_Type> define_set(std::string klassName = ""); 8 | } 9 | 10 | #include "set.ipp" 11 | 12 | #endif // Rice__stl__set__hpp_ -------------------------------------------------------------------------------- /rice/stl/shared_ptr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__shared_ptr__hpp_ 2 | #define Rice__stl__shared_ptr__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | template 7 | class Wrapper> : public WrapperBase 8 | { 9 | public: 10 | Wrapper(const std::shared_ptr& data); 11 | ~Wrapper(); 12 | void* get() override; 13 | std::shared_ptr& data(); 14 | 15 | private: 16 | std::shared_ptr data_; 17 | }; 18 | } 19 | 20 | namespace Rice 21 | { 22 | template 23 | Data_Type> define_shared_ptr(std::string klassName = ""); 24 | } 25 | 26 | #include "shared_ptr.ipp" 27 | 28 | #endif // Rice__stl__shared_ptr__hpp_ -------------------------------------------------------------------------------- /rice/stl/string.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__string__hpp_ 2 | #define Rice__stl__string__hpp_ 3 | 4 | #include "string.ipp" 5 | 6 | #endif // Rice__stl__string__hpp_ -------------------------------------------------------------------------------- /rice/stl/string_view.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__string_view__hpp_ 2 | #define Rice__stl__string_view__hpp_ 3 | 4 | #include "string_view.ipp" 5 | 6 | #endif // Rice__stl__string_view__hpp_ -------------------------------------------------------------------------------- /rice/stl/string_view.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::detail 4 | { 5 | template<> 6 | struct Type 7 | { 8 | static bool verify() 9 | { 10 | return true; 11 | } 12 | }; 13 | 14 | template<> 15 | class To_Ruby 16 | { 17 | public: 18 | VALUE convert(std::string_view const& x) 19 | { 20 | return detail::protect(rb_external_str_new, x.data(), (long)x.size()); 21 | } 22 | }; 23 | 24 | template<> 25 | class To_Ruby 26 | { 27 | public: 28 | VALUE convert(std::string_view const& x) 29 | { 30 | return detail::protect(rb_external_str_new, x.data(), (long)x.size()); 31 | } 32 | }; 33 | 34 | template<> 35 | class From_Ruby 36 | { 37 | public: 38 | From_Ruby() = default; 39 | 40 | explicit From_Ruby(Arg* arg) : arg_(arg) 41 | { 42 | } 43 | 44 | Convertible is_convertible(VALUE value) 45 | { 46 | switch (rb_type(value)) 47 | { 48 | case RUBY_T_STRING: 49 | return Convertible::Exact; 50 | break; 51 | default: 52 | return Convertible::None; 53 | } 54 | } 55 | 56 | std::string_view convert(VALUE value) 57 | { 58 | detail::protect(rb_check_type, value, (int)T_STRING); 59 | return std::string_view(RSTRING_PTR(value), RSTRING_LEN(value)); 60 | } 61 | 62 | private: 63 | Arg* arg_ = nullptr; 64 | }; 65 | } -------------------------------------------------------------------------------- /rice/stl/tuple.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__tuple__hpp_ 2 | #define Rice__stl__tuple__hpp_ 3 | 4 | #include "tuple.ipp" 5 | 6 | #endif // Rice__stl__tuple__hpp_ -------------------------------------------------------------------------------- /rice/stl/type_index.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__type_index__hpp_ 2 | #define Rice__stl__type_index__hpp_ 3 | 4 | #include "type_index.ipp" 5 | 6 | #endif // Rice__stl__type_index__hpp_ -------------------------------------------------------------------------------- /rice/stl/type_index.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::stl 4 | { 5 | inline Data_Type define_type_index() 6 | { 7 | Module rb_mStd = define_module("Std"); 8 | return define_class_under(rb_mStd, "TypeIndex"). 9 | define_constructor(Constructor()). 10 | define_method("hash_code", &std::type_index::hash_code). 11 | define_method("name", &std::type_index::name); 12 | } 13 | } 14 | 15 | namespace Rice::detail 16 | { 17 | template<> 18 | struct Type 19 | { 20 | static bool verify() 21 | { 22 | if (!detail::Registries::instance.types.isDefined()) 23 | { 24 | stl::define_type_index(); 25 | } 26 | 27 | return true; 28 | } 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /rice/stl/type_info.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__type_info__hpp_ 2 | #define Rice__stl__type_info__hpp_ 3 | 4 | #include "type_info.ipp" 5 | 6 | #endif // Rice__stl__type_info__hpp_ -------------------------------------------------------------------------------- /rice/stl/type_info.ipp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace Rice::stl 4 | { 5 | inline Data_Type define_type_info() 6 | { 7 | Module rb_mStd = define_module("Std"); 8 | return define_class_under(rb_mStd, "TypeInfo"). 9 | define_method("hash_code", &std::type_info::hash_code). 10 | define_method("name", &std::type_info::name); 11 | } 12 | } 13 | 14 | namespace Rice::detail 15 | { 16 | template<> 17 | struct Type 18 | { 19 | static inline bool verify() 20 | { 21 | if (!detail::Registries::instance.types.isDefined()) 22 | { 23 | stl::define_type_info(); 24 | } 25 | 26 | return true; 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /rice/stl/unique_ptr.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__unique_ptr__hpp_ 2 | #define Rice__stl__unique_ptr__hpp_ 3 | 4 | namespace Rice::detail 5 | { 6 | template 7 | class Wrapper> : public WrapperBase 8 | { 9 | public: 10 | Wrapper(std::unique_ptr&& data); 11 | ~Wrapper(); 12 | void* get() override; 13 | std::unique_ptr& data(); 14 | 15 | private: 16 | std::unique_ptr data_; 17 | }; 18 | } 19 | 20 | #include "unique_ptr.ipp" 21 | 22 | #endif // Rice__stl__unique_ptr__hpp_ -------------------------------------------------------------------------------- /rice/stl/unordered_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__unordered_map__hpp_ 2 | #define Rice__stl__unordered_map__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | Data_Type> define_unordered_map(std::string name = ""); 8 | } 9 | 10 | #include "unordered_map.ipp" 11 | 12 | #endif // Rice__stl__unordered_map__hpp_ -------------------------------------------------------------------------------- /rice/stl/variant.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__variant__hpp_ 2 | #define Rice__stl__variant__hpp_ 3 | 4 | #include "variant.ipp" 5 | 6 | #endif // Rice__stl__variant__hpp_ -------------------------------------------------------------------------------- /rice/stl/vector.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__stl__vector__hpp_ 2 | #define Rice__stl__vector__hpp_ 3 | 4 | namespace Rice 5 | { 6 | template 7 | Data_Type> define_vector(std::string name = "" ); 8 | } 9 | 10 | #include "vector.ipp" 11 | 12 | #endif // Rice__stl__vector__hpp_ -------------------------------------------------------------------------------- /rice/traits/attribute_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__attribute_traits__hpp_ 2 | #define Rice__detail__attribute_traits__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | // Base class 9 | template 10 | struct attribute_traits; 11 | 12 | template 13 | struct attribute_traits 14 | { 15 | using attr_type = Attr_T; 16 | using class_type = std::nullptr_t; 17 | }; 18 | 19 | template 20 | struct attribute_traits 21 | { 22 | using attr_type = Attr_T; 23 | using class_type = Class_T; 24 | }; 25 | } 26 | #endif // Rice__detail__attribute_traits__hpp_ 27 | -------------------------------------------------------------------------------- /rice/traits/method_traits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice__detail__method_traits__hpp_ 2 | #define Rice__detail__method_traits__hpp_ 3 | 4 | #include 5 | 6 | namespace Rice::detail 7 | { 8 | // Declare struct 9 | template 10 | struct method_traits; 11 | 12 | // Functions that do not have a self parameter: 13 | // doSomething(int a) 14 | template 15 | struct method_traits> 16 | { 17 | using Return_T = typename function_traits::return_type; 18 | using Class_T = std::nullptr_t; 19 | using Arg_Ts = typename function_traits::arg_types; 20 | static constexpr std::size_t arity = std::tuple_size_v; 21 | }; 22 | 23 | /* Functions that have a self parameter and thus we treat them as free standing 24 | "methods" versus member functions. 25 | 26 | doSomething(VALUE self, int a) */ 27 | template 28 | struct method_traits::class_type, std::nullptr_t>>> 29 | { 30 | using Return_T = typename function_traits::return_type; 31 | using Class_T = typename function_traits::template nth_arg<0>; 32 | using Arg_Ts = typename tuple_shift::arg_types>::type; 33 | static constexpr std::size_t arity = std::tuple_size_v; 34 | }; 35 | 36 | // Member functions that have an implied self parameter of an object instance 37 | // foo.doSomething(int a) 38 | template 39 | struct method_traits::class_type, std::nullptr_t>>> 40 | { 41 | using Return_T = typename function_traits::return_type; 42 | using Class_T = typename function_traits::class_type; 43 | using Arg_Ts = typename function_traits::arg_types; 44 | static constexpr std::size_t arity = std::tuple_size_v; 45 | }; 46 | } 47 | #endif // Rice__detail__method_traits__hpp_ 48 | -------------------------------------------------------------------------------- /sample/callbacks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (sample_callbacks SHARED 3 | "sample_callbacks.cpp") 4 | 5 | target_include_directories(sample_callbacks PRIVATE ../../include) 6 | target_include_directories(sample_callbacks PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(sample_callbacks PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(sample_callbacks ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /sample/callbacks/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('sample_callbacks') 5 | 6 | -------------------------------------------------------------------------------- /sample/callbacks/sample_callbacks.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace Rice; 4 | 5 | namespace 6 | { 7 | 8 | class CallbackHolder 9 | { 10 | public: 11 | 12 | void registerCallback(Rice::Object cb) 13 | { 14 | callback_ = cb; 15 | } 16 | 17 | Rice::Object fireCallback(Rice::String param) 18 | { 19 | return callback_.call("call", param); 20 | } 21 | 22 | Rice::Object callback_; 23 | }; 24 | 25 | } // namespace 26 | 27 | extern "C" 28 | void Init_sample_callbacks() 29 | { 30 | define_class("CallbackHolder") 31 | .define_constructor(Constructor()) 32 | .define_method("register_callback", &CallbackHolder::registerCallback) 33 | .define_method("fire_callback", &CallbackHolder::fireCallback); 34 | } 35 | 36 | -------------------------------------------------------------------------------- /sample/callbacks/test.rb: -------------------------------------------------------------------------------- 1 | require 'sample_callbacks' 2 | 3 | def hello(message) 4 | "Hello #{message}" 5 | end 6 | 7 | cb1 = CallbackHolder.new 8 | cb2 = CallbackHolder.new 9 | cb3 = CallbackHolder.new 10 | 11 | cb1.register_callback(lambda do |param| 12 | "Callback 1 got param #{param}" 13 | end) 14 | 15 | cb2.register_callback(lambda do |param| 16 | "Callback 2 got param #{param}" 17 | end) 18 | 19 | cb3.register_callback method(:hello) 20 | 21 | puts "Calling Callback 1" 22 | puts cb1.fire_callback("Hello") 23 | 24 | puts "Calling Callback 2" 25 | puts cb2.fire_callback("World") 26 | 27 | puts "Calling Callback 3" 28 | puts cb3.fire_callback("Ruby") 29 | -------------------------------------------------------------------------------- /sample/enum/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (sample_enum SHARED 3 | "sample_enum.cpp") 4 | 5 | target_include_directories(sample_enum PRIVATE ../../include) 6 | target_include_directories(sample_enum PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(sample_enum PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(sample_enum ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /sample/enum/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('sample_enum') 5 | 6 | -------------------------------------------------------------------------------- /sample/enum/sample_enum.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace Rice; 4 | 5 | namespace 6 | { 7 | 8 | enum Sample_Enum 9 | { 10 | SE_FOO = 1, 11 | SE_BAR = 42, 12 | SE_BAZ = 100, 13 | }; 14 | 15 | char const * description(Sample_Enum e) 16 | { 17 | switch(e) 18 | { 19 | case SE_FOO: return "Fairly Ordinary Object"; 20 | case SE_BAR: return "Beginner's All-purpose Ratchet"; 21 | case SE_BAZ: return "Better than A Zebra"; 22 | } 23 | return "???"; 24 | } 25 | 26 | } // namespace 27 | 28 | extern "C" 29 | void Init_sample_enum() 30 | { 31 | Rice::Enum sample_enum_type = 32 | define_enum("Sample_Enum") 33 | .define_value("FOO", SE_FOO) 34 | .define_value("BAR", SE_BAR) 35 | .define_value("BAZ", SE_BAZ); 36 | 37 | sample_enum_type 38 | .define_method("description", description); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /sample/enum/test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'sample_enum' 2 | 3 | Sample_Enum.each { |x| p x } 4 | s = Sample_Enum::FOO 5 | puts s 6 | puts s.inspect 7 | puts s < Sample_Enum::BAR 8 | s.description 9 | -------------------------------------------------------------------------------- /sample/inheritance/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (animals SHARED 3 | "animals.cpp") 4 | 5 | target_include_directories(animals PRIVATE ../../include) 6 | target_include_directories(animals PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(animals PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(animals ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /sample/inheritance/animals.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace Rice; 4 | 5 | class Organism 6 | { 7 | public: 8 | virtual ~Organism() = default; 9 | virtual char const * name() = 0; 10 | }; 11 | 12 | class Animal 13 | : public Organism 14 | { 15 | public: 16 | virtual char const * speak() = 0; 17 | }; 18 | 19 | class Bear 20 | : public Animal 21 | { 22 | public: 23 | char const * name() override 24 | { 25 | return "Bear"; 26 | } 27 | 28 | char const * speak() override 29 | { 30 | return "I'm smarter than the average bear"; 31 | } 32 | }; 33 | 34 | class Dog 35 | : public Animal 36 | { 37 | public: 38 | char const * name() override 39 | { 40 | return "Dog"; 41 | } 42 | 43 | char const * speak() override 44 | { 45 | return "Woof woof"; 46 | } 47 | }; 48 | 49 | class Rabbit 50 | : public Animal 51 | { 52 | public: 53 | char const * name() override 54 | { 55 | return "Rabbit"; 56 | } 57 | 58 | char const * speak() override 59 | { 60 | return "What's up, doc?"; 61 | } 62 | }; 63 | 64 | extern "C" 65 | void Init_animals(void) 66 | { 67 | define_class("Organism") 68 | .define_method("name", &Organism::name); 69 | 70 | define_class("Animal") 71 | .define_method("speak", &Animal::speak); 72 | 73 | define_class("Bear") 74 | .define_constructor(Constructor()); 75 | 76 | define_class("Dog") 77 | .define_constructor(Constructor()); 78 | 79 | define_class("Rabbit") 80 | .define_constructor(Constructor()); 81 | } 82 | 83 | -------------------------------------------------------------------------------- /sample/inheritance/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('animals') 5 | 6 | -------------------------------------------------------------------------------- /sample/inheritance/test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'animals' 2 | 3 | [ Bear, Dog, Rabbit].each do |klass| 4 | animal = klass.new 5 | puts "A #{animal.name} says: #{animal.speak}" 6 | end 7 | 8 | -------------------------------------------------------------------------------- /sample/map/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (map SHARED 3 | "map.cpp") 4 | 5 | target_include_directories(map PRIVATE ../../include) 6 | target_include_directories(map PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(map PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(map ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /sample/map/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('map') 5 | 6 | -------------------------------------------------------------------------------- /sample/map/map.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | namespace 8 | { 9 | 10 | class Map 11 | { 12 | private: 13 | struct Ruby_Value_Compare 14 | { 15 | bool operator()(Object lhs, Object rhs) const 16 | { 17 | Object result = lhs.call("<", rhs); 18 | return result.test(); 19 | } 20 | }; 21 | 22 | typedef std::map Value_Map; 23 | 24 | public: 25 | Object bracket(Object k) 26 | { 27 | Value_Map::iterator it = map_.find(k); 28 | return it == map_.end() ? Object(Qnil) : it->second; 29 | } 30 | 31 | Object bracket_equals(Object k, Object v) 32 | { 33 | map_[k] = v; 34 | return Qnil; 35 | } 36 | 37 | Value_Map::iterator begin() { return map_.begin(); } 38 | Value_Map::iterator end() { return map_.end(); } 39 | 40 | typedef Value_Map::value_type value_type; 41 | typedef Value_Map::iterator iterator; 42 | 43 | private: 44 | Value_Map map_; 45 | }; 46 | 47 | } // namespace 48 | 49 | template<> 50 | struct detail::To_Ruby 51 | { 52 | static VALUE convert(Map::value_type const & pair) 53 | { 54 | return detail::protect(rb_assoc_new, pair.first.value(), pair.second.value()); 55 | } 56 | }; 57 | 58 | #include 59 | extern "C" 60 | void Init_map(void) 61 | { 62 | Rice::Module rb_mStd = define_module("Std"); 63 | 64 | // TODO: no delete method on the map, because I'm not sure how to 65 | // make delete work properly while iterating 66 | Data_Type rb_cMap = 67 | define_class_under(rb_mStd, "Map") 68 | .define_constructor(Constructor()) 69 | .define_method("[]", &Map::bracket) 70 | .define_method("[]=", &Map::bracket_equals) 71 | .define_iterator(&Map::begin, &Map::end) 72 | .include_module(rb_mEnumerable); 73 | } 74 | -------------------------------------------------------------------------------- /sample/map/test.rb: -------------------------------------------------------------------------------- 1 | require_relative 'map.so' 2 | m = Std::Map.new 3 | m[0] = 1 4 | m[1] = 2 5 | m[3] = 3 6 | m.each { |x| p x } 7 | 8 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | unittest 2 | vm_unittest 3 | -------------------------------------------------------------------------------- /test/embed_ruby.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void embed_ruby() 5 | { 6 | static bool initialized__ = false; 7 | 8 | if (!initialized__) 9 | { 10 | RUBY_INIT_STACK; 11 | 12 | int argc = 0; 13 | char* argv = nullptr; 14 | char** pArgv = &argv; 15 | 16 | ruby_sysinit(&argc, &pArgv); 17 | 18 | ruby_init(); 19 | ruby_init_loadpath(); 20 | 21 | #if RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 1 22 | // Force the prelude / builtins 23 | const char* opts[] = { "ruby", "-e;" }; 24 | ruby_options(2, (char**)opts); 25 | #endif 26 | 27 | initialized__ = true; 28 | 29 | // Initialize Rice 30 | Rice::init(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/embed_ruby.hpp: -------------------------------------------------------------------------------- 1 | #ifndef Rice_embed_ruby 2 | #define Rice_embed_ruby 3 | void embed_ruby(); 4 | #endif 5 | -------------------------------------------------------------------------------- /test/ext/t1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (t1 SHARED 3 | "t1.cpp") 4 | 5 | target_include_directories(t1 PRIVATE ../../..) 6 | target_include_directories(t1 PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(t1 PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(t1 ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /test/ext/t1/Foo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef T1__FOO__HPP_ 2 | #define T1__FOO__HPP_ 3 | 4 | class Foo 5 | { 6 | public: 7 | int foo() { return 42; } 8 | }; 9 | 10 | #endif // T1__FOO__HPP_ 11 | -------------------------------------------------------------------------------- /test/ext/t1/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('t1') 5 | -------------------------------------------------------------------------------- /test/ext/t1/t1.cpp: -------------------------------------------------------------------------------- 1 | #include "Foo.hpp" 2 | #include 3 | 4 | using namespace Rice; 5 | 6 | extern "C" 7 | void Init_t1() 8 | { 9 | define_class("Foo") 10 | .define_constructor(Constructor()) 11 | .define_method("foo", &Foo::foo); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test/ext/t2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Add source to this project's executable. 2 | add_library (t2 SHARED 3 | "t2.cpp") 4 | 5 | target_include_directories(t2 PRIVATE ../../..) 6 | target_include_directories(t2 PRIVATE ${Ruby_INCLUDE_DIR} ${Ruby_CONFIG_INCLUDE_DIR}) 7 | target_include_directories(t2 PRIVATE ${PROJECT_SOURCE_DIR}) 8 | target_link_libraries(t2 ${Ruby_LIBRARY}) -------------------------------------------------------------------------------- /test/ext/t2/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | 4 | create_makefile('t2') 5 | -------------------------------------------------------------------------------- /test/ext/t2/t2.cpp: -------------------------------------------------------------------------------- 1 | #include "../t1/Foo.hpp" 2 | #include 3 | 4 | using namespace Rice; 5 | 6 | extern "C" 7 | void Init_t2() 8 | { 9 | volatile Data_Type foo; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /test/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'bundler/setup' 2 | require 'mkmf-rice' 3 | require 'rbconfig' 4 | 5 | abort "libffi not found" unless have_libffi 6 | 7 | # Totally hack mkmf to make a unittest executable instead of a shared library 8 | target_exe = "unittest#{RbConfig::CONFIG['EXEEXT']}" 9 | $cleanfiles << target_exe 10 | 11 | create_makefile(target_exe) do |conf| 12 | conf << "\n" 13 | conf << "#{target_exe}: $(OBJS)" 14 | conf << "\t$(ECHO) linking executable unittest" 15 | conf << "\t-$(Q)$(RM) $(@)" 16 | 17 | if IS_MSWIN 18 | conf << "\t$(Q) $(CXX) -Fe$(@) $(OBJS) $(LIBS) $(LOCAL_LIBS) -link $(ldflags) $(LIBPATH)" 19 | else 20 | conf << "\t$(Q) $(CXX) -o $@ $(OBJS) $(LIBPATH) $(LOCAL_LIBS) $(LIBS)" 21 | end 22 | 23 | conf << "\n" 24 | end 25 | -------------------------------------------------------------------------------- /test/ruby/test_callbacks_sample.rb: -------------------------------------------------------------------------------- 1 | $: << File.join(File.dirname(__FILE__), '..', 'sample') 2 | 3 | require 'rubygems' 4 | gem 'minitest' 5 | require 'minitest/autorun' 6 | require_relative '../../sample/callbacks/sample_callbacks' 7 | 8 | class CallbacksTest < Minitest::Test 9 | def test_callbacks_sample_lambda 10 | cb = CallbackHolder.new 11 | cb.register_callback(lambda do |param| 12 | "Callback got: #{param}" 13 | end) 14 | 15 | assert_equal "Callback got: Hello", cb.fire_callback("Hello") 16 | end 17 | 18 | def hello_world(param) 19 | "Method got: #{param}" 20 | end 21 | 22 | def test_callbacks_sample_method 23 | cb = CallbackHolder.new 24 | cb.register_callback method(:hello_world) 25 | 26 | assert_equal "Method got: Hello", cb.fire_callback("Hello") 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /test/ruby/test_multiple_extensions.rb: -------------------------------------------------------------------------------- 1 | $: << File.join(__dir__, '..', '..', 'sample') 2 | 3 | require 'minitest' 4 | require 'minitest/autorun' 5 | 6 | class MultipleExtensionTest < Minitest::Test 7 | def test_multiple_extensions 8 | # Rinse 9 | require 'map/map' 10 | m = Std::Map.new 11 | m[0] = 1 12 | 13 | # And repeat 14 | require 'enum/sample_enum' 15 | m = Std::Map.new 16 | m[0] = 1 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/ruby/test_multiple_extensions_same_class.rb: -------------------------------------------------------------------------------- 1 | $: << File.join(__dir__, '..', 'ext') 2 | 3 | require 'minitest' 4 | require 'minitest/autorun' 5 | 6 | class MultipleExtensionsSameClassTest < Minitest::Test 7 | def test_multiple_extensions_same_class 8 | require 't1/t1' 9 | require 't2/t2' 10 | 11 | foo = Foo.new 12 | assert_equal 42, foo.foo 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/ruby/test_multiple_extensions_with_inheritance.rb: -------------------------------------------------------------------------------- 1 | $: << File.join(__dir__, '..', '..', 'sample') 2 | # Load a library that uses inheritance 3 | require 'inheritance/animals' 4 | 5 | # Then load a different library 6 | require 'enum/sample_enum' 7 | 8 | require 'minitest' 9 | require 'minitest/autorun' 10 | 11 | class MultipleExtensionTest < Minitest::Test 12 | def test_multiple_extensions_with_inheritance 13 | 14 | # And make sure we can call methods in the base class on a derived 15 | # instance 16 | dog = Dog.new 17 | assert_equal dog.name, "Dog" 18 | assert_equal dog.speak, "Woof woof" 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /test/test_Address_Registration_Guard.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Address_Registration_Guard); 8 | 9 | SETUP(Address_Registration_Guard) 10 | { 11 | embed_ruby(); 12 | } 13 | 14 | TEARDOWN(Address_Registration_Guard) 15 | { 16 | rb_gc_start(); 17 | } 18 | 19 | TESTCASE(register_address) 20 | { 21 | VALUE v = Qnil; 22 | Address_Registration_Guard g(&v); 23 | } 24 | 25 | TESTCASE(register_object) 26 | { 27 | Object o; 28 | Address_Registration_Guard g(&o); 29 | } 30 | 31 | TESTCASE(get_address) 32 | { 33 | VALUE v = Qnil; 34 | Address_Registration_Guard g(&v); 35 | ASSERT_EQUAL(&v, g.address()); 36 | } 37 | 38 | TESTCASE(move_construct) 39 | { 40 | VALUE value = detail::to_ruby("Value 1"); 41 | 42 | Address_Registration_Guard guard1(&value); 43 | Address_Registration_Guard guard2(std::move(guard1)); 44 | 45 | ASSERT((guard1.address() == nullptr)); 46 | ASSERT_EQUAL(&value, guard2.address()); 47 | } 48 | 49 | TESTCASE(move_assign) 50 | { 51 | VALUE value1 = detail::to_ruby("Value 1"); 52 | VALUE value2 = detail::to_ruby("Value 2"); 53 | 54 | Address_Registration_Guard guard1(&value1); 55 | Address_Registration_Guard guard2(&value2); 56 | 57 | guard2 = std::move(guard1); 58 | 59 | ASSERT((guard1.address() == nullptr)); 60 | ASSERT_EQUAL(&value1, guard2.address()); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /test/test_Builtin_Object.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Builtin_Object); 8 | 9 | SETUP(Builtin_Object) 10 | { 11 | embed_ruby(); 12 | } 13 | 14 | TEARDOWN(Builtin_Object) 15 | { 16 | rb_gc_start(); 17 | } 18 | 19 | TESTCASE(construct_with_object) 20 | { 21 | Class c(rb_cObject); 22 | Object o(c.call("new")); 23 | Builtin_Object b(o); 24 | ASSERT_EQUAL(o.value(), b.value()); 25 | ASSERT_EQUAL(T_OBJECT, rb_type(b.value())); 26 | ASSERT_EQUAL(rb_cObject, b.class_of().value()); 27 | ASSERT_EQUAL(rb_cObject, CLASS_OF(b.value())); 28 | } 29 | 30 | TESTCASE(copy_construct) 31 | { 32 | Class c(rb_cObject); 33 | Object o(c.call("new")); 34 | Builtin_Object b(o); 35 | Builtin_Object b2(b); 36 | ASSERT_EQUAL(o.value(), b2.value()); 37 | ASSERT_EQUAL(T_OBJECT, rb_type(b2.value())); 38 | ASSERT_EQUAL(rb_cObject, b2.class_of().value()); 39 | ASSERT_EQUAL(rb_cObject, CLASS_OF(b2.value())); 40 | } 41 | 42 | TESTCASE(copy_assign) 43 | { 44 | Class c(rb_cObject); 45 | Builtin_Object b1(c.call("new")); 46 | Builtin_Object b2(c.call("new")); 47 | 48 | b2 = b1; 49 | 50 | ASSERT_EQUAL(b2.value(), b1.value()); 51 | } 52 | 53 | TESTCASE(move_constructor) 54 | { 55 | Class c(rb_cObject); 56 | Builtin_Object b1(c.call("new")); 57 | Builtin_Object b2(std::move(b1)); 58 | 59 | ASSERT_NOT_EQUAL(b2.value(), b1.value()); 60 | ASSERT_EQUAL(b1.value(), Qnil); 61 | } 62 | 63 | TESTCASE(move_assign) 64 | { 65 | Class c(rb_cObject); 66 | Builtin_Object b1(c.call("new")); 67 | Builtin_Object b2(c.call("new")); 68 | 69 | b2 = std::move(b1); 70 | 71 | ASSERT_NOT_EQUAL(b2.value(), b1.value()); 72 | ASSERT_EQUAL(b1.value(), Qnil); 73 | } 74 | 75 | TESTCASE(dereference) 76 | { 77 | Class c(rb_cObject); 78 | Object o(c.call("new")); 79 | Builtin_Object b(o); 80 | ASSERT_EQUAL(ROBJECT(o.value()), &*b); 81 | } 82 | 83 | TESTCASE(arrow) 84 | { 85 | Class c(rb_cObject); 86 | Object o(c.call("new")); 87 | Builtin_Object b(o); 88 | ASSERT_EQUAL(rb_cObject, b->basic.klass); 89 | } 90 | 91 | TESTCASE(get) 92 | { 93 | Class c(rb_cObject); 94 | Object o(c.call("new")); 95 | Builtin_Object b(o); 96 | ASSERT_EQUAL(ROBJECT(o.value()), b.get()); 97 | } 98 | -------------------------------------------------------------------------------- /test/test_File.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "unittest.hpp" 5 | #include "embed_ruby.hpp" 6 | #include 7 | #include 8 | 9 | using namespace Rice; 10 | 11 | TESTSUITE(FILE); 12 | 13 | SETUP(FILE) 14 | { 15 | embed_ruby(); 16 | } 17 | 18 | TEARDOWN(FILE) 19 | { 20 | rb_gc_start(); 21 | } 22 | 23 | namespace 24 | { 25 | FILE* openFile() 26 | { 27 | std::filesystem::path path = __FILE__; 28 | FILE* fptr = fopen(path.string().c_str(), "rb"); 29 | return fptr; 30 | } 31 | 32 | std::string readFile(FILE* fptr) 33 | { 34 | std::ostringstream result; 35 | 36 | char buffer[255]; 37 | while (fgets(buffer, sizeof(buffer), fptr) != NULL) 38 | { 39 | result << buffer; 40 | } 41 | return result.str(); 42 | } 43 | 44 | bool closeFile(FILE* fptr) 45 | { 46 | // Ruby override fclose and replaces it with rb_w32_fclose which causes a segementation fault. Oy! 47 | #ifndef _MSC_VER 48 | fclose(fptr); 49 | #endif 50 | return true; 51 | } 52 | } 53 | 54 | TESTCASE(File) 55 | { 56 | Module m = define_module("TestingModule"); 57 | m.define_module_function("open_file", openFile). 58 | define_module_function("read_file", readFile). 59 | define_module_function("close_file", closeFile); 60 | 61 | Data_Object file = m.call("open_file"); 62 | ASSERT((file.value() != Qnil)); 63 | ASSERT((file.get() != nullptr)); 64 | 65 | String string = m.call("read_file", file); 66 | ASSERT((string.length() > 1300)); 67 | 68 | Object result = m.call("close_file", file); 69 | ASSERT_EQUAL(Qtrue, result.value()); 70 | } -------------------------------------------------------------------------------- /test/test_Identifier.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Identifier); 8 | 9 | SETUP(Identifier) 10 | { 11 | embed_ruby(); 12 | } 13 | 14 | TEARDOWN(Identifier) 15 | { 16 | rb_gc_start(); 17 | } 18 | 19 | TESTCASE(construct_from_id) 20 | { 21 | ID id = rb_intern("foo"); 22 | Identifier identifier(id); 23 | ASSERT_EQUAL(id, identifier.id()); 24 | } 25 | 26 | TESTCASE(construct_from_symbol) 27 | { 28 | Symbol symbol("FOO"); 29 | Identifier identifier(symbol); 30 | ASSERT_EQUAL(rb_intern("FOO"), identifier.id()); 31 | } 32 | 33 | TESTCASE(construct_from_c_string) 34 | { 35 | Identifier identifier("Foo"); 36 | ASSERT_EQUAL(rb_intern("Foo"), identifier.id()); 37 | } 38 | 39 | TESTCASE(construct_from_string) 40 | { 41 | Identifier identifier(std::string("Foo")); 42 | ASSERT_EQUAL(rb_intern("Foo"), identifier.id()); 43 | } 44 | 45 | TESTCASE(copy_construct) 46 | { 47 | Identifier identifier1("Foo"); 48 | Identifier identifier2(identifier1); 49 | ASSERT_EQUAL(rb_intern("Foo"), identifier2.id()); 50 | } 51 | 52 | TESTCASE(c_str) 53 | { 54 | Identifier identifier("Foo"); 55 | ASSERT_EQUAL("Foo", identifier.c_str()); 56 | } 57 | 58 | TESTCASE(str) 59 | { 60 | Identifier identifier("Foo"); 61 | ASSERT_EQUAL(std::string("Foo"), identifier.str()); 62 | } 63 | 64 | TESTCASE(implicit_conversion_to_id) 65 | { 66 | Identifier identifier("Foo"); 67 | ASSERT_EQUAL(rb_intern("Foo"), static_cast(identifier)); 68 | } 69 | 70 | TESTCASE(to_sym) 71 | { 72 | Identifier identifier("Foo"); 73 | ASSERT_EQUAL(Symbol("Foo"), Symbol(identifier.to_sym())); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /test/test_Jump_Exception.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Jump_Tag); 8 | 9 | SETUP(Jump_Tag) 10 | { 11 | } 12 | 13 | TEARDOWN(Jump_Tag) 14 | { 15 | rb_gc_start(); 16 | } 17 | 18 | TESTCASE(construct) 19 | { 20 | JumpException exception(JumpException::RUBY_TAG_RETURN); 21 | ASSERT_EQUAL(JumpException::RUBY_TAG_RETURN, exception.tag); 22 | } 23 | 24 | -------------------------------------------------------------------------------- /test/test_Keep_Alive_No_Wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Keep_Alive_No_Wrapper); 8 | 9 | namespace 10 | { 11 | class Animal 12 | { 13 | public: 14 | Animal(char const * name) : name_(name) {} 15 | char const * getName() { return name_; } 16 | virtual ~Animal() = default; 17 | private: 18 | char const * name_; 19 | }; 20 | 21 | class Zoo 22 | { 23 | public: 24 | Zoo(void) 25 | { 26 | pets_.push_back(new Animal("Bear")); 27 | pets_.push_back(new Animal("Tiger")); 28 | pets_.push_back(new Animal("Lion")); 29 | } 30 | 31 | ~Zoo() 32 | { 33 | for(auto pet : pets_) 34 | { 35 | delete pet; 36 | } 37 | pets_.clear(); 38 | } 39 | 40 | Object getPets(void) { 41 | Array pets; 42 | for(auto p: pets_) { 43 | pets.push(p); 44 | } 45 | return pets; 46 | } 47 | 48 | private: 49 | std::vector pets_; 50 | }; 51 | } 52 | 53 | SETUP(Keep_Alive_No_Wrapper) 54 | { 55 | embed_ruby(); 56 | } 57 | 58 | TEARDOWN(Keep_Alive_No_Wrapper) 59 | { 60 | rb_gc_start(); 61 | } 62 | 63 | TESTCASE(test_keep_alive_no_wrapper) 64 | { 65 | define_class("Animal") 66 | .define_constructor(Constructor()) 67 | .define_method("get_name", &Animal::getName); 68 | 69 | define_class("Zoo") 70 | .define_constructor(Constructor()) 71 | .define_method("get_pets", &Zoo::getPets, Return().keepAlive()); 72 | 73 | Module m = define_module("TestingModule"); 74 | Object zoo = m.module_eval("@zoo = Zoo.new"); 75 | 76 | // get_pets returns an Array (builtin type) so Return().keepAlive() 77 | // shall result in std::runtime_error 78 | ASSERT_EXCEPTION_CHECK( 79 | Exception, 80 | m.module_eval("@zoo.get_pets.each do |pet| puts pet.name; end"), 81 | ASSERT_EQUAL("When calling the method `get_pets' we could not find the wrapper for the 'Array' return type. You should not use keepAlive() on a Return or Arg that is a builtin Rice type.", 82 | ex.what()) 83 | ); 84 | } 85 | -------------------------------------------------------------------------------- /test/test_Memory_Management.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Memory_Management); 8 | 9 | SETUP(Memory_Management) 10 | { 11 | embed_ruby(); 12 | } 13 | 14 | TEARDOWN(Memory_Management) 15 | { 16 | rb_gc_start(); 17 | } 18 | 19 | namespace 20 | { 21 | class TestClass { 22 | double tmp; 23 | public: 24 | TestClass() {tmp=0;} 25 | 26 | double getTmp() { 27 | return tmp; 28 | } 29 | 30 | void setTmp(double x) { 31 | tmp = x; 32 | } 33 | }; 34 | 35 | TestClass returnTestClass() { 36 | TestClass x = TestClass(); 37 | x.setTmp(8); 38 | return x; 39 | } 40 | } 41 | 42 | TESTCASE(allows_copy_contructors_to_work) 43 | { 44 | define_class("TestClass") 45 | .define_method("tmp=", &TestClass::setTmp) 46 | .define_method("tmp", &TestClass::getTmp); 47 | 48 | define_global_function("return_test_class", &returnTestClass); 49 | 50 | Module m = define_module("TestingModule"); 51 | 52 | Object result = m.module_eval("return_test_class.tmp"); 53 | ASSERT_EQUAL(8.0, detail::From_Ruby().convert(result.value())); 54 | } 55 | -------------------------------------------------------------------------------- /test/test_Native_Registry.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | 4 | #include 5 | 6 | using namespace Rice; 7 | 8 | TESTSUITE(NativeRegistry); 9 | 10 | SETUP(NativeRegistry) 11 | { 12 | embed_ruby(); 13 | } 14 | 15 | TEARDOWN(NativeRegistry) 16 | { 17 | rb_gc_start(); 18 | } 19 | -------------------------------------------------------------------------------- /test/test_Proc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "unittest.hpp" 4 | #include "embed_ruby.hpp" 5 | #include 6 | 7 | using namespace Rice; 8 | 9 | TESTSUITE(Proc); 10 | 11 | SETUP(Proc) 12 | { 13 | embed_ruby(); 14 | } 15 | 16 | TEARDOWN(Proc) 17 | { 18 | rb_gc_start(); 19 | } 20 | 21 | namespace 22 | { 23 | int square(int i) 24 | { 25 | return i * i; 26 | } 27 | 28 | auto squareProc() 29 | { 30 | return square; 31 | } 32 | } 33 | 34 | TESTCASE(SquareProc) 35 | { 36 | Module m = define_module("TestingModuleMakeProc"); 37 | m.define_module_function("square_proc", squareProc); 38 | 39 | std::string code = R"(proc = square_proc 40 | proc.call(9))"; 41 | 42 | Object result = m.module_eval(code); 43 | ASSERT_EQUAL(81, detail::From_Ruby().convert(result)); 44 | } 45 | -------------------------------------------------------------------------------- /test/test_Stl_Exception.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "unittest.hpp" 5 | #include "embed_ruby.hpp" 6 | #include 7 | #include 8 | 9 | using namespace Rice; 10 | 11 | TESTSUITE(StlException); 12 | 13 | SETUP(StlException) 14 | { 15 | embed_ruby(); 16 | } 17 | 18 | TEARDOWN(StlException) 19 | { 20 | rb_gc_start(); 21 | } 22 | 23 | namespace 24 | { 25 | class MyException: public std::exception 26 | { 27 | }; 28 | 29 | void raiseMyException() 30 | { 31 | throw MyException(); 32 | } 33 | 34 | } 35 | 36 | TESTCASE(StlExceptionCreate) 37 | { 38 | Class MyExceptionKlass = define_class("MyException"); 39 | 40 | auto handler = [MyExceptionKlass]() 41 | { 42 | try 43 | { 44 | throw; 45 | } 46 | catch (const MyException& exception) 47 | { 48 | Data_Object wrapper(exception, true); 49 | rb_exc_raise(wrapper.value()); 50 | } 51 | }; 52 | detail::Registries::instance.handlers.set(handler); 53 | 54 | Module m = define_module("Testing"); 55 | m.define_singleton_function("raise_my_exception", &raiseMyException); 56 | 57 | std::string code = R"(begin 58 | raise_my_exception 59 | rescue MyException => exception 60 | $! 61 | end)"; 62 | 63 | Object object = m.instance_eval(code); 64 | ASSERT_EQUAL(MyExceptionKlass.value(), object.class_of().value()); 65 | } 66 | 67 | namespace 68 | { 69 | std::exception_ptr createExceptionPtr() 70 | { 71 | std::exception_ptr eptr; 72 | 73 | try 74 | { 75 | [[maybe_unused]] 76 | char ch = std::string().at(1); // this generates a std::out_of_range 77 | } 78 | catch (...) 79 | { 80 | eptr = std::current_exception(); // capture 81 | } 82 | 83 | return eptr; 84 | } 85 | 86 | void handleExceptionPtr(std::exception_ptr eptr) 87 | { 88 | std::rethrow_exception(eptr); 89 | } 90 | } 91 | 92 | TESTCASE(StlExceptionPtr) 93 | { 94 | Module m = define_module("TestingModule"); 95 | m.define_module_function("create_exception_ptr", createExceptionPtr). 96 | define_module_function("handle_exception_ptr", handleExceptionPtr); 97 | 98 | Data_Object exception = m.call("create_exception_ptr"); 99 | VALUE value = exception.value(); 100 | std::exception_ptr* ptr = exception.get(); 101 | ASSERT((value != Qnil)); 102 | ASSERT((ptr != nullptr)); 103 | 104 | ASSERT_EXCEPTION_CHECK( 105 | Exception, 106 | m.call("handle_exception_ptr", exception), 107 | ASSERT_EQUAL(rb_eIndexError, ex.class_of()) 108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /test/test_Stl_Optional.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | using namespace Rice; 9 | 10 | TESTSUITE(Optional); 11 | 12 | namespace 13 | { 14 | class MyClass 15 | { 16 | public: 17 | std::optional optionalReturn(bool flag) 18 | { 19 | if (flag) 20 | { 21 | return std::string("Here is a value"); 22 | } 23 | else 24 | { 25 | return std::nullopt; 26 | } 27 | } 28 | 29 | int optionalArgument(std::optional data) 30 | { 31 | return data ? data.value() : 7; 32 | } 33 | 34 | std::optional optional_ = std::nullopt; 35 | }; 36 | } 37 | 38 | Class makeOptionalClass() 39 | { 40 | return define_class("MyClass"). 41 | define_constructor(Constructor()). 42 | define_method("optional_return", &MyClass::optionalReturn). 43 | define_method("optional_argument", &MyClass::optionalArgument). 44 | define_attr("optional_attr", &MyClass::optional_); 45 | } 46 | 47 | SETUP(Optional) 48 | { 49 | embed_ruby(); 50 | makeOptionalClass(); 51 | } 52 | 53 | TEARDOWN(Optional) 54 | { 55 | rb_gc_start(); 56 | } 57 | 58 | TESTCASE(OptionalReturn) 59 | { 60 | Module m = define_module("Testing"); 61 | Object myClass = m.module_eval("MyClass.new"); 62 | 63 | Object result = myClass.call("optional_return", true); 64 | ASSERT_EQUAL("Here is a value", detail::From_Ruby().convert(result)); 65 | 66 | result = myClass.call("optional_return", false); 67 | ASSERT_EQUAL(Qnil, result.value()); 68 | } 69 | 70 | TESTCASE(OptionalArgument) 71 | { 72 | Module m = define_module("Testing"); 73 | Object myClass = m.module_eval("MyClass.new"); 74 | 75 | Object result = myClass.call("optional_argument", 77); 76 | ASSERT_EQUAL(77, detail::From_Ruby().convert(result)); 77 | 78 | result = myClass.call("optional_argument", std::nullopt); 79 | ASSERT_EQUAL(7, detail::From_Ruby().convert(result)); 80 | } 81 | 82 | TESTCASE(OptionalAttribute) 83 | { 84 | Module m = define_module("Testing"); 85 | Object myClass = m.module_eval("MyClass.new"); 86 | 87 | Object result = myClass.call("optional_attr"); 88 | ASSERT_EQUAL(Qnil, result.value()); 89 | 90 | result = myClass.call("optional_attr=", 77.7); 91 | ASSERT_EQUAL(77.7, detail::From_Ruby().convert(result)); 92 | 93 | result = myClass.call("optional_attr=", std::nullopt); 94 | ASSERT_EQUAL(Qnil, result.value()); 95 | } 96 | -------------------------------------------------------------------------------- /test/test_Stl_String.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using namespace Rice; 10 | 11 | TESTSUITE(StlString); 12 | 13 | SETUP(StlString) 14 | { 15 | embed_ruby(); 16 | } 17 | 18 | TEARDOWN(StlString) 19 | { 20 | rb_gc_start(); 21 | } 22 | 23 | TESTCASE(std_string_to_ruby) 24 | { 25 | ASSERT(rb_equal(String("").value(), detail::to_ruby(std::string("")))); 26 | ASSERT(rb_equal(String("foo").value(), detail::to_ruby(std::string("foo")))); 27 | } 28 | 29 | TESTCASE(std_string_to_ruby_encoding) 30 | { 31 | VALUE value = detail::to_ruby(std::string("Some String")); 32 | Object object(value); 33 | Object encoding = object.call("encoding"); 34 | Object encodingName = encoding.call("name"); 35 | std::string result = detail::From_Ruby().convert(encodingName); 36 | if(result != "ASCII-8BIT" && result != "US-ASCII" && result != "UTF-8") { 37 | FAIL("Encoding incorrect", "ASCII-8BIT, US-ASCII, or UTF-8 (Windows)", result); 38 | } 39 | } 40 | 41 | TESTCASE(std_string_to_ruby_encoding_utf8) 42 | { 43 | rb_encoding* defaultEncoding = rb_default_external_encoding(); 44 | 45 | VALUE utf8Encoding = rb_enc_from_encoding(rb_utf8_encoding()); 46 | rb_enc_set_default_external(utf8Encoding); 47 | 48 | VALUE value = detail::to_ruby(std::string("Some String")); 49 | Object object(value); 50 | Object encoding = object.call("encoding"); 51 | Object encodingName = encoding.call("name"); 52 | ASSERT_EQUAL("UTF-8", detail::From_Ruby().convert(encodingName)); 53 | 54 | rb_enc_set_default_external(rb_enc_from_encoding(defaultEncoding)); 55 | } 56 | 57 | TESTCASE(std_string_from_ruby) 58 | { 59 | ASSERT_EQUAL(std::string(""), detail::From_Ruby().convert(rb_str_new2(""))); 60 | ASSERT_EQUAL(std::string("foo"), detail::From_Ruby().convert(rb_str_new2("foo"))); 61 | 62 | ASSERT_EXCEPTION_CHECK( 63 | Exception, 64 | detail::From_Ruby().convert(rb_float_new(15.512)), 65 | ASSERT_EQUAL("wrong argument type Float (expected String)", ex.what()) 66 | ); 67 | } 68 | 69 | TESTCASE(std_string_to_ruby_with_binary) 70 | { 71 | Rice::String got = detail::to_ruby(std::string("\000test", 5)); 72 | 73 | ASSERT_EQUAL(String(std::string("\000test", 5)), got); 74 | ASSERT_EQUAL(5ul, got.length()); 75 | } 76 | 77 | TESTCASE(std_string_from_ruby_with_binary) 78 | { 79 | std::string got = detail::From_Ruby().convert(rb_str_new("\000test", 5)); 80 | ASSERT_EQUAL(5ul, got.length()); 81 | ASSERT_EQUAL(std::string("\000test", 5), got); 82 | } 83 | -------------------------------------------------------------------------------- /test/test_Symbol.cpp: -------------------------------------------------------------------------------- 1 | #include "unittest.hpp" 2 | #include "embed_ruby.hpp" 3 | #include 4 | 5 | using namespace Rice; 6 | 7 | TESTSUITE(Symbol); 8 | 9 | SETUP(Symbol) 10 | { 11 | embed_ruby(); 12 | } 13 | 14 | TEARDOWN(Symbol) 15 | { 16 | rb_gc_start(); 17 | } 18 | 19 | TESTCASE(construct_from_symbol) 20 | { 21 | VALUE v = ID2SYM(rb_intern("foo")); 22 | Symbol symbol(v); 23 | ASSERT_EQUAL(v, symbol.value()); 24 | } 25 | 26 | TESTCASE(construct_from_identifier) 27 | { 28 | Identifier identifier("FOO"); 29 | Symbol symbol(identifier); 30 | ASSERT_EQUAL(ID2SYM(rb_intern("FOO")), symbol.value()); 31 | } 32 | 33 | TESTCASE(construct_from_string) 34 | { 35 | Symbol symbol("Foo"); 36 | ASSERT_EQUAL(ID2SYM(rb_intern("Foo")), symbol.value()); 37 | } 38 | 39 | TESTCASE(construct_from_string_view) 40 | { 41 | std::string_view view("Foo"); 42 | Symbol symbol(view); 43 | ASSERT_EQUAL(ID2SYM(rb_intern("Foo")), symbol.value()); 44 | } 45 | 46 | TESTCASE(default_construct) 47 | { 48 | Symbol symbol; 49 | ASSERT_EQUAL(ID2SYM(rb_intern("")), symbol.value()); 50 | } 51 | 52 | TESTCASE(copy_construct) 53 | { 54 | Symbol symbol1("Foo"); 55 | Symbol symbol2(symbol1); 56 | ASSERT_EQUAL(ID2SYM(rb_intern("Foo")), symbol2.value()); 57 | } 58 | 59 | TESTCASE(c_str) 60 | { 61 | Symbol symbol("Foo"); 62 | ASSERT_EQUAL("Foo", symbol.c_str()); 63 | } 64 | 65 | TESTCASE(str) 66 | { 67 | Symbol symbol("Foo"); 68 | ASSERT_EQUAL(std::string("Foo"), symbol.str()); 69 | } 70 | 71 | TESTCASE(to_id) 72 | { 73 | Symbol symbol("Foo"); 74 | ASSERT_EQUAL(Identifier("Foo"), symbol.to_id()); 75 | } 76 | --------------------------------------------------------------------------------