rttrorg / rttr

C++ Reflection Library
https://www.rttr.org
MIT License
3.17k stars 439 forks source link

Forcing Compile-Time Type Registration #208

Open jkunstwald opened 5 years ago

jkunstwald commented 5 years ago

I want to use RTTR to get a unique ID for a type (class), serialize it, deserialize it, and then instantiate that class (on a different machine, over the network).

Since type::get_id() is not unique across process executions, my idea is to use type::get_name(). A string is too long, so i want to hash the strings and cache them. This cache should be complete right after the process launches, its content is known at compile time. Im using a singleton which iterates over type::get_derived_classes() in its constructor (of a specific class type of interest).

However, the derived class types are not available immediately, some sort of interaction with each derived type has to have come first. Is there a clean way of forcing the RTTR registration to fully register a type?

In the simplest example, you have Foo.h:

#pragma once
#include <rttr/type>

class Foo { RTTR_ENABLE() };
class DerivedFoo: public Foo { RTTR_ENABLE(Foo) };

and Foo.cpp:

#include "Foo.h"
#include <rttr/registration>

RTTR_REGISTRATION
{
    rttr::registration::class_<Foo>("Foo").constructor<>();
    rttr::registration::class_<DerivedFoo>("DerivedFoo").constructor<>();
}

Now, if nothing has happened with either Foo or DerivedFoo (an instantiation or a call to type::get<DerivedFoo>) before constructing the singleton, it will not find DerivedFoo. And even then, if the singleton is included before Foo.h, it will also not find DerivedFoo. Is there an elegant solution to this issue, or am i missing something else entirely which makes this unnecessary?

acki-m commented 5 years ago

The type is available to RTTR, when this code here:

RTTR_REGISTRATION
{
    rttr::registration::class_<Foo>("Foo").constructor<>();
    rttr::registration::class_<DerivedFoo>("DerivedFoo").constructor<>();
}

gets executed. And this code here will be automatically executed, before main is called. Does this answer your question?

jkunstwald commented 5 years ago

Huh, i have found my issue: My .cpp files with the RTTR_REGISTRATION blocks are part of a library which gets declared as

add_library(my-library STATIC ${SOURCE_FILES} ${HEADER_FILES})

and then linked to in the executable

target_link_libraries(my-executable PRIVATE my-library)

And it in this case it looks like, the RTTR_REGISTRATION blocks are not (unconditionally) executed before the my-executable main is called.

Did you ever encounter this issue? Can something be done about this? If i add a dummy executable to the library itself with all of the .cpp files as CMake sources, the blocks execute just fine before its main is called.

acki-m commented 5 years ago

You mean that the RTTR_REGISTRATION part get optimized out?

jkunstwald commented 5 years ago

Yes, i think so.

CosmicRey commented 5 years ago

Hey, I have also been having issues with RTTR_REGISTRATION not being invoked before main() when linked from a static library, I am currently testing in debug in MSVC in C+17. Any ideas why this could be happening?

Update: I was able to fix my problem... Ensure that the linker options "Use Library Dependency Inputs" and "Link Library Dependencies" are enabled

TheAifam5 commented 5 years ago

In Visual Studio you can use "Use library Dependency Inputs" in the "Linker > General" to fix that problem. In GCC just link with .o objects.

You can try with __attribute__ ((used)).

emxaz commented 5 years ago

Does anyone have a general solution to this that doesn't rely on specific linker flags? I seem to be running into the same problem: the registration logic for things defined in a static library is not being invoked unconditionally in a consuming application. If I add a dummy function to the same translation unit and invoke that from my consuming application, then the registration logic DOES run.

DavidColson commented 5 years ago

@emxaz the method of registering as shown in this issue solves this, though it's a more verbose way of registering types: https://github.com/rttrorg/rttr/issues/106

MickAlmighty commented 2 years ago

I had the same problem with MSVC, but in my case when I use CMake I could't set "Use library Dependency Inputs" flag in the Visual Studio project's linker properties. For MSVC CMake project the solution was to link the static library to the executable in such way: target_link_libraries(exec static_lib -WHOLEARCHIVE:$)

MSDN reference: https://docs.microsoft.com/en-us/cpp/build/reference/wholearchive-include-all-library-object-files?view=msvc-170