Created
April 29, 2015 08:43
-
-
Save target-san/fa9901975848aa9edd9c to your computer and use it in GitHub Desktop.
ADL-based static init for arbitrary set of types
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <typeinfo> | |
/** This sketch is derived from Cereal's polymorphic serialization logic | |
Type registration: | |
https://github.com/USCiLab/cereal/blob/master/include/cereal/types/polymorphic.hpp | |
Archive registration and binding details: | |
https://github.com/USCiLab/cereal/blob/master/include/cereal/details/polymorphic_impl.hpp | |
Archive registration macro: | |
https://github.com/USCiLab/cereal/blob/master/include/cereal/cereal.hpp | |
What it can be used for: | |
invoke certain code (at example, type registration) statically | |
for an arbitrary set of types | |
Utilizes: | |
1. ADL - argument-dependent lookup; search function implementation | |
in namespaces where its arguments' types are declared | |
2. Delayed overload lookup - reference undeclared function inside template function's body | |
to delay its resolution till template instantiation | |
3. Creation of static object and its construction if some type is instantiated | |
How it works | |
1. prvokeAdl is invoked | |
2. ADL performs lookup for all possible compatible overloads of adlHelper | |
3. Return types of compatible overloads instantiated, but only default one is called, | |
because 0 is better compatible with int than with nullptr | |
4. Each return type references pointer to instantiation of init template function | |
5. init function body is fully instantiated but not called for each return type | |
6. init function template contains reference to StaticObject, | |
which provokes instantiation of nested type and its construction at statics init time | |
7. Voila! We have some init code being invoked at pre-run time for each of arbitrary set of types | |
*/ | |
// Instantiates T's instance as static object, if referenced via getInstance | |
template <class T> | |
class StaticObject | |
{ | |
private: | |
//! Forces instantiation at pre-execution time | |
static void instantiate( T const & ) {} | |
static T & create() | |
{ | |
static T t; | |
instantiate(instance); | |
return t; | |
} | |
StaticObject( StaticObject const & /*other*/ ) {} | |
public: | |
static T & getInstance() | |
{ | |
return create(); | |
} | |
private: | |
static T & instance; | |
}; | |
template <class T> T & StaticObject<T>::instance = StaticObject<T>::create(); | |
/// Enables ADL lookup in this namespace | |
struct adl_tag { }; | |
/** Root function, provokes instantiation of all compatible overloads | |
In fact, only overloads' signatures are instantiated | |
This means that all return types of ADL overloads are instantiated | |
*/ | |
template<typename T> | |
void provokeAdl() | |
{ | |
adlHelper((T*)nullptr, 0, adl_tag { }); | |
} | |
/** Best overload. Will be always called, because 0 is closer to int than a nullptr | |
Other compatible overloads, when taken into consideration, will have all types in their signatures | |
being instantiated. Return types are important, because we don't need to reference them explicitly | |
*/ | |
template<typename T> void adlHelper(T*, int, adl_tag) { } | |
/// Forces any function passed to template argument via pointer to be instantiated | |
/// because we explicitly require pointer to it, which means we need its body | |
template<void (*)()> struct FunctionPinner { }; | |
/** Its 'type' typedef is used as return value for all considered overloads | |
This provokes instantiation of 'init' function bodies | |
*/ | |
template<typename T, typename U> | |
struct AdlVal | |
{ | |
static void init(); | |
using type = FunctionPinner<&AdlVal<T, U>::init>; | |
}; | |
/// Payload, constructed as static | |
template<typename T, typename U> | |
struct Bingo | |
{ | |
Bingo() | |
{ | |
std::cout << typeid(T).name() << ' ' << typeid(U).name() << '\n'; | |
} | |
}; | |
/// Init function, references payload | |
template<typename T, typename U> | |
void AdlVal<T, U>::init() | |
{ | |
StaticObject<Bingo<T, U> >::getInstance(); | |
} | |
/// Macro, declares ADL overload for specified type | |
#define ADL($type) template<typename T> typename AdlVal<T, $type>::type adlHelper(T*, $type*, adl_tag); | |
ADL(int); | |
ADL(float); | |
int main(int, char**) | |
{ | |
provokeAdl<char>(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment