Skip to content

Commit

Permalink
Add new library class (rttrorg#116)
Browse files Browse the repository at this point in the history
* This adds the functionatliy to load libraries in a simple cross plattform way.
The native OS calls are wrapped behind a class interface.

Highlights:

- handling of file suffixes (.dll or .so)
- automatic unload of library on program exit
- etrieving of loaded types in library
- avoid of unncessary loading calls, when same plugin is loaded multiple times

additional:

- added unit tests
- added example of loading a plugin
- added a small tutorial how do develop plugins with RTTR

Development notes:

* fixed invalid memory access under MacOSX with clang

Actually the problem was also present under linux, (with gcc or clang).
This situation was following:
1. A plugin was loaded with type "int[100]" and registered
2. The plugin was unloaded => "int[100]" was removed from the type system and deleted
3. The type[100] was used in the unit_test later and was accessing the memory
of the already deleted type_data

The reason for this behavior lies in the usage of local static member variables
(which is used to cache the retrieving and registering of type_data information).
This variable was process wide unique, this lead to problems when unloading a plugin.
The static was no reinitialized when unloading the plugin.
So the variable was pointing to type_data information which was alreadyl deleted.

Now we are using RTTR_LOCAL to hide the symbols for exporting. This fixes the issues.
  • Loading branch information
acki-m authored Mar 5, 2018
1 parent 79b619c commit 7232d52
Show file tree
Hide file tree
Showing 61 changed files with 2,680 additions and 273 deletions.
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,5 @@ build_script:
- cd build
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_BENCHMARKS=%BUILD_BENCHMARKS%
- if "%TESTS_ONLY%" == "true" (ninja run_tests)
- if "%BUILD_EXAMPLES%" == "true" (ninja json_serialization)
- if "%BUILD_EXAMPLES%" == "true" (ninja json_example && ninja library_loader_example)
- if "%BUILD_BENCHMARKS%" == "true" (ninja bench_method && ninja bench_rttr_cast && ninja bench_variant)
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ script:
- |
if [[ "${BUILD_EXAMPLES}" == "true" ]]; then
# Run unit tests on two cores
(cd build && make json_serialization -j2)
(cd build && make json_example -j2 && make library_loader_example -j2)
fi
- |
if [[ "${VALGRIND}" == "true" ]]; then
Expand Down
1 change: 1 addition & 0 deletions doc/md_pages/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ Registration of Types
-# [Default Arguments](@ref default_arguments_page)
-# [Parameter Names](@ref parameter_names_page)
-# [Policies](@ref register_policies_page)
-# [Plugins](@ref register_plugins)
3 changes: 2 additions & 1 deletion doc/md_pages/tutorial/five_minute_tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private:
};
}
~~~~
The standard include file of rttr is: `<rttr/type>`.<br>
The standard include file of rttr is: `<rttr/type>`<br>
Remark the two added macros: \ref RTTR_ENABLE() and \ref RTTR_REGISTRATION_FRIEND. They are optional.<br>
However, when you use class hierarchies you should add to every class: \ref RTTR_ENABLE().<br>
When you want to reflect private data of a class, add: \ref RTTR_REGISTRATION_FRIEND.
Expand Down Expand Up @@ -76,6 +76,7 @@ RTTR_REGISTRATION
;
}
~~~~
The standard include file for registration types in rttr is: `<rttr/registration>`<br>Include this file only when you want to register something.
The \ref rttr::registration "registration" class is here the entry point.
This registration process creates internally wrapper classes, which store for example function pointers or object pointers of the specific class.
For these pointers you have to provide the data manually.
Expand Down
2 changes: 1 addition & 1 deletion doc/md_pages/tutorial/policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,4 +274,4 @@ then bind the wrapper instead of the original accessor.

<hr>

<div type="button" class="btn btn-default doxy-button">[previous](@ref parameter_names_page "Parameter Names")</div><div class="btn btn-default doxy-button">[Finished](@ref tutorial_page "Tutorial")</div>
<div type="button" class="btn btn-default doxy-button">[previous](@ref parameter_names_page "Parameter Names")</div><div class="btn btn-default doxy-button">[next](@ref register_plugins "Register Plugins")</div>
107 changes: 107 additions & 0 deletions doc/md_pages/tutorial/register_plugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
Register Plugins {#register_plugins}
=================
RTTR has build in support to register your types into shared libraries, which can be loaded
and unloaded at runtime. Furthermore, it has a simple wrapper class called \ref rttr::library "library"
which wraps the platform dependent calls to load the library.

See following example:
~~~~{.cpp}
#include <rttr/registration>
struct MyPluginClass
{
MyPluginClass(){}
void perform_calculation()
{
value += 12;
}
void perform_calculation(int new_value)
{
value += new_value;
}
int value = 0;
};
RTTR_PLUGIN_REGISTRATION // remark the different registration macro!
{
rttr::registration::class_<MyPluginClass>("MyPluginClass")
.constructor<>()
.property("value", &MyPluginClass::value)
.method("perform_calculation", rttr::select_overload<void(void)>(&MyPluginClass::perform_calculation))
.method("perform_calculation", rttr::select_overload<void(int)>(&MyPluginClass::perform_calculation))
;
}
~~~~
In order to register your types inside a plugin, you have to use the macro
\ref RTTR_PLUGIN_REGISTRATION.
Then the containing code will be executed every time you load the library
and it makes sure to unregister yours types, when your library will be unloaded.

Now the following code will load the plugin into your application:
~~~~{.cpp}
#include <rttr/type>
int main(int argc, char** argv)
{
using namespace rttr;
// no suffix is needed, RTTR will automatically append the platform specific file suffix
library lib("MyPlugin");
if (!lib.load())
{
std::cerr << lib.get_error_string() << std::endl;
return -1;
}
// print all classes contained in the library
for (auto t : lib.get_types()) // returns all registered types from this library
{
if (t.is_class() && !t.is_wrapper())
std::cout << t.get_name() << std::endl;
}
// we cannot use the actual type, to get the type information,
// thus we use string to retrieve it
auto t = type::get_by_name("MyPluginClass");
// iterate over all methods of the class
for (auto meth : t.get_methods())
{
std::cout << meth.get_signature() << std::endl;
}
// work with the new type
auto var = t.create();
t.invoke("perform_calculation", var, {});
std::cout << t.get_property_value("value", var).to_int() << std::endl; // prints "12"
return 0;
}
~~~~
Output:
~~~~{.cpp}
MyPluginClass
perform_calculation( )
perform_calculation( int )
12
~~~~
\remark When you compile your plugin with the `gcc` toolchain, make sure you use the compiler option: `-fno-gnu-unique`.
otherwise the unregistration will not work properly.

Summary
-------
- Using plugins you can work with types without having access to the concrete type itself
- You don't have to explicit export (e.g. using `__declspec( dllexport )`) your types in the shared library
- You can export classes, without a generic abstract interface
- You can export overloaded methods (not possible in C)
- With all this functionality, it is easily possible to implement \b hot-reload of shared libraries.
You could serialize your object into JSON-Format, unload the library, load the new version and deserialize it again.
- \remark Make sure you throw away all retrieved items (\ref rttr::type "types", \ref rttr::property "properties", \ref rttr::method "methods" etc...) of the loaded library when unloading.
Otherwise UB may occur. (e.g. Invoking a method of an unloaded library is not possible)

<hr>

<div type="button" class="btn btn-default doxy-button">[previous](@ref register_policies_page "Register Policies")</div><div class="btn btn-default doxy-button">[finished](@ref tutorial_page "Tutorial")</div>
8 changes: 4 additions & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
# the benchmarks
#############################################

add_subdirectory (rttr)
add_subdirectory(rttr)

if (BUILD_UNIT_TESTS)
add_subdirectory (unit_tests)
add_subdirectory(unit_tests)
endif()

if (BUILD_BENCHMARKS)
add_subdirectory (benchmarks)
add_subdirectory(benchmarks)
endif()

if (BUILD_EXAMPLES)
add_subdirectory (examples)
add_subdirectory(examples)
endif()

1 change: 1 addition & 0 deletions src/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@
####################################################################################

add_subdirectory (json_serialization)
add_subdirectory (library_loading)
1 change: 1 addition & 0 deletions src/examples/ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ You can find more information on: <a target="_blank" href="http://www.rttr.org">

##### Following examples are available
1. serialization/deserialization with json
2. explicit loading of libraries at runtime
21 changes: 11 additions & 10 deletions src/examples/json_serialization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,30 @@
# #
####################################################################################

project(json_serialization LANGUAGES CXX)
project(json_example LANGUAGES CXX)

message(STATUS "Scanning " ${PROJECT_NAME} " module.")
message(STATUS "===========================")

generateLibraryVersionVariables(${RTTR_VERSION_MAJOR} ${RTTR_VERSION_MINOR} ${RTTR_VERSION_PATCH}
"RTTR Examples: json serialization" "Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org>" "MIT License")

loadFolder("json" HPP_FILES SRC_FILES)
loadFolder("files" HPP_FILES SRC_FILES)

if (USE_PCH)
activate_precompiled_headers("pch.h" SRC_FILES)
endif()

add_executable(json_serialization ${SRC_FILES} ${HPP_FILES})
target_link_libraries(json_serialization RTTR::Core)
target_include_directories(json_serialization PUBLIC ${RAPID_JSON_DIR})
set_target_properties(json_serialization PROPERTIES DEBUG_POSTFIX ${RTTR_DEBUG_POSTFIX}
FOLDER "Examples"
INSTALL_RPATH "${RTTR_EXECUTABLE_INSTALL_RPATH}"
CXX_STANDARD ${MAX_CXX_STANDARD})
add_executable(json_example ${SRC_FILES} ${HPP_FILES})
target_link_libraries(json_example RTTR::Core)
target_include_directories(json_example PUBLIC ${RAPID_JSON_DIR})
set_target_properties(json_example
PROPERTIES DEBUG_POSTFIX ${RTTR_DEBUG_POSTFIX}
FOLDER "Examples"
INSTALL_RPATH "${RTTR_EXECUTABLE_INSTALL_RPATH}"
CXX_STANDARD ${MAX_CXX_STANDARD})

set_compiler_warnings(json_serialization)
set_compiler_warnings(json_example)

message(STATUS "Scanning " ${PROJECT_NAME} " module finished!")
message(STATUS "")
File renamed without changes.
29 changes: 29 additions & 0 deletions src/examples/library_loading/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
####################################################################################
# #
# Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org> #
# #
# This file is part of RTTR (Run Time Type Reflection) #
# License: MIT License #
# #
# Permission is hereby granted, free of charge, to any person obtaining #
# a copy of this software and associated documentation files (the "Software"), #
# to deal in the Software without restriction, including without limitation #
# the rights to use, copy, modify, merge, publish, distribute, sublicense, #
# and/or sell copies of the Software, and to permit persons to whom the #
# Software is furnished to do so, subject to the following conditions: #
# #
# The above copyright notice and this permission notice shall be included in #
# all copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
# SOFTWARE. #
# #
####################################################################################

add_subdirectory (library_loader_example)
add_subdirectory (plugin_example)
51 changes: 51 additions & 0 deletions src/examples/library_loading/library_loader_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
####################################################################################
# #
# Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org> #
# #
# This file is part of the examples of RTTR (Run Time Type Reflection) #
# License: MIT License #
# #
# Permission is hereby granted, free of charge, to any person obtaining #
# a copy of this software and associated documentation files (the "Software"), #
# to deal in the Software without restriction, including without limitation #
# the rights to use, copy, modify, merge, publish, distribute, sublicense, #
# and/or sell copies of the Software, and to permit persons to whom the #
# Software is furnished to do so, subject to the following conditions: #
# #
# The above copyright notice and this permission notice shall be included in #
# all copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
# SOFTWARE. #
# #
####################################################################################

project(library_loader_example LANGUAGES CXX)

message(STATUS "Scanning " ${PROJECT_NAME} " module.")
message(STATUS "===========================")

generateLibraryVersionVariables(${RTTR_VERSION_MAJOR} ${RTTR_VERSION_MINOR} ${RTTR_VERSION_PATCH}
"RTTR Examples: library_loading" "Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org>" "MIT License")

loadFolder("files" HPP_FILES SRC_FILES)

if (USE_PCH)
activate_precompiled_headers("pch.h" SRC_FILES)
endif()

add_executable(library_loader_example ${SRC_FILES} ${HPP_FILES})
target_link_libraries(library_loader_example RTTR::Core)
add_dependencies(library_loader_example plugin_example)

set_target_properties(library_loader_example PROPERTIES DEBUG_POSTFIX ${RTTR_DEBUG_POSTFIX}
FOLDER "Examples/library_loading")


message(STATUS "Scanning " ${PROJECT_NAME} " module finished!")
message(STATUS "")
6 changes: 6 additions & 0 deletions src/examples/library_loading/library_loader_example/ReadMe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Plugin loading
==============

This example demonstrate the explicit loading of libraries.


32 changes: 32 additions & 0 deletions src/examples/library_loading/library_loader_example/files.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
####################################################################################
# #
# Copyright (c) 2014, 2015 - 2017 Axel Menzel <info@rttr.org> #
# #
# This file is part of the examples of RTTR (Run Time Type Reflection) #
# License: MIT License #
# #
# Permission is hereby granted, free of charge, to any person obtaining #
# a copy of this software and associated documentation files (the "Software"), #
# to deal in the Software without restriction, including without limitation #
# the rights to use, copy, modify, merge, publish, distribute, sublicense, #
# and/or sell copies of the Software, and to permit persons to whom the #
# Software is furnished to do so, subject to the following conditions: #
# #
# The above copyright notice and this permission notice shall be included in #
# all copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
# SOFTWARE. #
# #
####################################################################################

set(HEADER_FILES version.rc.in
)

set(SOURCE_FILES main.cpp
)
Loading

0 comments on commit 7232d52

Please sign in to comment.