Skip to content

Instantly share code, notes, and snippets.

@target-san
Created April 29, 2015 08:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save target-san/fa9901975848aa9edd9c to your computer and use it in GitHub Desktop.
Save target-san/fa9901975848aa9edd9c to your computer and use it in GitHub Desktop.
ADL-based static init for arbitrary set of types
#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