I have two callbacks foo and bar, both taking a generic value each.
When both callbacks has been called I want these values to meet in single function. My test code is:
template <class FooType, class BarType>
void meet_point(FooType foo_value, BarType bar_value) {
// Success
}
struct test
{
template <class FooType>
void foo(FooType value) {
// TODO: save value or type somehow
}
template <class BarType>
void bar(BarType value) {
meet_point(/* FooType value from above */, value);
}
};
template <class F>
void sync_request(F&& f) {
f("foobar");
}
int main()
{
test t;
sync_request([&t](auto value) {
t.foo(value);
});
t.bar(42);
}
Is there any way to do it?
EDIT:
Note that code in main() is just an example of calling both callbacks. Actual calls happens from different places and both triggers do not know anything about each other.
Also at the time of instantiating test object no type is known ahead. Actually test struct is not even needed here. It could be standalone callbacks. It is there just for maybe possible storage of FooType value.
P.S.
I wish we had reflection, jit, Circle #type_id or templated virtual functions to extract the type in C++.
You need to know (or be able to exhaustively list the possibilities of) at least one of FooType or BarType.
Case 1: FooType is one of typename... FooTypes known when you instantiate a test.
template<typename... FooTypes>
struct test
{
using FooType = std::variant<FooTypes...>;
void foo(FooType value) {
foo_value = value;
}
template <class BarType>
void bar(BarType value) {
std::variant<BarType> bar_value = value;
std::visit(meet_point, foo_value, bar_value);
}
FooType foo_value;
};
Case 2: BarType is one of typename... BarTypes known when you instantiate a test.
template<typename... BarTypes>
struct test
{
using BarType = std::variant<BarTypes...>;
template <class FooType>
void foo(FooType value) {
std::variant<FooType> foo_value = value;
visit = [foo_value](BarType bar_value){ std::visit(meet_point, foo_value, bar_value); };
}
void bar(BarType value) {
visit(value)
}
function<void(std::variant<BarTypes...>)> visit;
};
Case 3: FooType is one of typename... FooTypes and BarType is one of typename... BarTypes, both known when you instantiate a test.
template<typename... FooTypes, typename... BarTypes>
struct test
{
using FooType = std::variant<FooTypes...>;
using BarType = std::variant<BarTypes...>;
void foo(FooType value) {
foo_value = value;
}
void bar(BarType bar_value) {
std::visit(meet_point, foo_value, bar_value);
}
std::variant<FooTypes...> foo_value;
};
When you write your code you do not know which types the user of your library will use. But when the user compiles his types has to be known. If you are fine with letting the user register his types you can do it like this:
#include <boost/algorithm/algorithm.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/hana.hpp>
#include <functional>
#include <iostream>
#include <type_traits>
// code you write
struct CallbackData
{
virtual ~CallbackData () = default;
};
// code your user writes for example in userTypes.h
struct TestFooType : CallbackData
{
int value = 42;
};
struct TestBarType : CallbackData
{
std::string value{ "bar type" };
};
struct AnotherTestFooType : CallbackData
{
int value = 42;
};
struct AnotherTestBarType : CallbackData
{
std::string value{ "bar type" };
};
static boost::hana::tuple<TestFooType, TestBarType, AnotherTestFooType, AnotherTestBarType> const sharedClasses{};
// code you write
struct test
{
void
bothAreReady ()
{
boost::hana::for_each (sharedClasses, [&] (auto x) {
if (std::shared_ptr<std::decay_t<decltype (x)>> firstValue = std::dynamic_pointer_cast<std::decay_t<decltype (x)>> (fooValue))
{
std::cout << "fooValue: " << firstValue->value << std::endl;
return;
}
});
boost::hana::for_each (sharedClasses, [&] (auto x) {
if (std::shared_ptr<std::decay_t<decltype (x)>> secondValue = std::dynamic_pointer_cast<std::decay_t<decltype (x)>> (barValue))
{
std::cout << "barValue: " << secondValue->value << std::endl;
return;
}
});
}
bool
isDone ()
{
return fooValue && barValue;
}
std::shared_ptr<CallbackData> fooValue{};
std::shared_ptr<CallbackData> barValue{};
};
// code your user writes
int
main ()
{
test t;
auto fooAction = [&t] () {
auto testFooType = TestFooType{};
// do something with test foo type
t.fooValue = std::shared_ptr<CallbackData>{ new std::decay_t<decltype (testFooType)>{ std::move (testFooType) } };
if (t.isDone ()) t.bothAreReady ();
};
auto barAction = [&t] () {
auto testBarType = TestBarType{};
// do something with testBarType
t.barValue = std::shared_ptr<CallbackData>{ new std::decay_t<decltype (testBarType)>{ std::move (testBarType) } };
if (t.isDone ()) t.bothAreReady ();
};
// run the lambdas on some other thread or so
fooAction ();
barAction ();
}
wandbox example
Alice live in Australia. Alice creates a nice class Alice, writes a source file that contains a call to test.foo(Alice()), builds a DLL, and ships it to you.
Bob lives in Bermuda. Bob creates a nice class Bob, writes a source file that contains a call to test.bar(Bob()), builds a DLL, and ships it to you.
Alice and Bob have never heard of each other and have never seen each other's code.
You have your program load the two DLLs and...
Where is meet_point<Alice, Bob>?
It cannot be in Alice's DLL, because Alice have never seen Bob's code.
It cannot be in Bob's DLL, because Bob have never seen Alice's code.
It cannot be in your program, because you have never seen the code of either of them! All you have is their DLL that call your code. They both obviously have your header that defines struct test, but this doesn't help at all.
So you need some tool that would go over Alice's and Bob's code, extract all types that are used in calls to foo and bar, and instantiate meet_point<a, b> with every possible pair (a, b) of such types. The problem is, there isn't such tool in any standard toolkit. You need to write one somehow.
The easiest way would be asking Alice and Bob to send you a list of their types used in calls to foo and bar. You can also go over their object code and extract these types from symbols or whatever. In any case you need them to send you header files with definitions of all their classes, so that you can compile each instantiation. Another option (not as crazy as it sounds) would be just-in-time compilation: make your class find the headers (you can know the names of the classes so that's doable), generate a source that instantiates meet_point with relevant arguments, run the compiler, build a DLL, load it, and call the function! Cache the DLL for later use. Of course it could happen that meet_point cannot be compiled with a particular combination of arguments, and you will only know it when this combination arrives.
Now "two developers that do not talk to each other send you DLLs" is an extreme model of the situation, but it doesn't actually create any new problem. It just makes it easier to see problems that are inherent in the situation.
Related
I have a function that takes a T and calls specific functions on the supplied object. Until now it was used from compile-time objects, so all was great. Minimal example:
#include <iostream>
struct A {
void fun() const { std::cout << "A" << std::endl; }
};
struct B {
void fun() const { std::cout << "B" << std::endl; }
};
template<class T>
void use_function(const T& param) {
param.fun();
}
int main() {
use_function(A{}); // "A"
use_function(B{}); // "B"
return 0;
}
Now I'm trying to use that use_function() with objects that get created at runtime and having a hard time. I can't use std::variant or std::any since I need to supply the type as template parameter for their access functions - although all their variants fulfil the function interface. Example for a (failing) variant approach:
using var_type = std::variant<A, B>;
struct IdentityVisitor {
template<class T>
auto operator()(const T& alternative) const -> T {
return alternative;
}
};
int main() {
var_type var = A{};
// error C2338: visit() requires the result of all potential invocations to have the same type and value category (N4828 [variant.visit]/2).
use_function(std::visit(IdentityVisitor{}, var));
return 0;
}
What is possible is directly calling the function with an appropriate type like this:
if (rand() % 2 == 0)
use_function(A{});
else
use_function(B{});
just storing it in between is what I can't get working.
I understand on a technical level but having trouble coming up with an elegant solution. Is there one? I know that I could rewrite the objects with even a lightweight inheritance - but was trying to see if it's feasible to avoid it altogether, even if just as an exercise to avoid OOP in favor of templates and concepts. I feel like variants should be working with this, but apparently not.
std::visit([](auto const& x) { use_function(x); }, var);
If overload sets were objects, you could pass use_function to std::visit directly. Because they aren't, you need to wrap it in something that will be instantiated as a call to the right overload.
std::visit([](auto const& x) { use_function(x); }, var);
I have an attribute vector that can hold different types:
class base_attribute_vector; // no template args
template<typename T>
class raw_attribute_vector : public base_attribute_vector;
raw_attribute_vector<int> foo;
raw_attribute_vector<std::string> foo;
Based on run-time input for the type, I would like to create the appropriate data structure. Pseudocode:
std::string type("int");
raw_attribute_vector<type> foo;
Obviously, this fails. An easy, but ugly and unmaintainable workaround is a run-time switch/chained if:
base_attribute_vector *foo;
if(type == "int") foo = new raw_attribute_vector<int>;
else if(type == "string") ...
I read about run-time polymorphism with functors, but found it quite complex for a task that is conceptually easy.
What is the best and cleanest way to make this work? I played around with boost::hana, finding that while I can create a mapping from string to type, the lookup can only be done at compile time:
auto types =
hana::make_map(
hana::make_pair(BOOST_HANA_STRING("int"), hana::type_c<int>),
hana::make_pair(BOOST_HANA_STRING("string"), hana::type_c<std::string>)
);
All possible types are known at compile-time. Any suggestions are highly appreciated. In a perfect solution, I would create the name->type mapping in a single place. Afterwards, I would use it like this
std::vector<base_attribute_vector*> foo;
foo.push_back(magic::make_templated<raw_attribute_vector, "int">);
foo.push_back(magic::make_templated<raw_attribute_vector, "std::string">);
foo[0]->insert(123);
foo[1]->insert("bla");
foo[0]->print();
foo[1]->print();
It is not required for this magic to happen at compile time. My goal is to have as readable code as possible.
I'd use an std::map that has strings as key and std::function as values. I would associate the string with a function that returns your type. Here's an example:
using functionType = std::function<std::unique_ptr<base_attribute_vector>()>;
std::map<std::string, functionType> theMap;
theMap.emplace("int", []{ return new raw_attribute_vector<int>; });
theMap.emplace("float", []{ return new raw_attribute_vector<float>; });
// Using the map
auto base_vec = theMap["int"](); // base_vec is an instance of raw_attribute_vector<int>
Of course, this solution is valid if you only know the string value at runtime.
enum class Type
{
Int,
String,
// ...
Unknown
};
Type TypeFromString(const std::string& s)
{
if (s == "int") { return Type::Int; }
if (s == "string") { return Type::String; }
// ...
return Type::Unknown;
}
template <template <typename> class>
struct base_of;
template <template <typename> class C>
using base_of_t = typename base_of<C>::type;
And then the generic factory
template <template <typename> class C>
std::unique_ptr<base_of_t<C>> make_templated(const std::string& typeStr)
{
Type type = TypeFromString(typeStr);
static const std::map<Type, std::function<std::unique_ptr<base_of_t<C>>()>> factory{
{Type::Int, [] { return std::make_unique<C<int>>(); } },
{Type::String, [] { return std::make_unique<C<std::string>>(); } },
// ...
{Type::Unknown, [] { return nullptr; } }
};
return factory.at(type)();
}
a specialization is needed for each base:
template <>
struct base_of<raw_attribute_vector> {
using type = base_attribute_vector;
};
And then
auto p = make_templated<raw_attribute_vector>(s);
Demo
I'd probably do something like this:
Features:
1 - time registration of objects by passing a named prototype
constant time lookup at runtime
lookup by any type which can be compared to std::string
-
#include <unordered_map>
#include <string>
struct base_attribute_vector { virtual ~base_attribute_vector() = default; };
template<class Type> struct attribute_vector : base_attribute_vector {};
// copyable singleton makes handling a breeze
struct vector_factory
{
using ptr_type = std::unique_ptr<base_attribute_vector>;
template<class T>
vector_factory add(std::string name, T)
{
get_impl()._generators.emplace(std::move(name),
[]() -> ptr_type
{
return std::make_unique< attribute_vector<T> >();
});
return *this;
}
template<class StringLike>
ptr_type create(StringLike&& s) const {
return get_impl()._generators.at(s)();
}
private:
using generator_type = std::function<ptr_type()>;
struct impl
{
std::unordered_map<std::string, generator_type, std::hash<std::string>, std::equal_to<>> _generators;
};
private:
static impl& get_impl() {
static impl _ {};
return _;
}
};
// one-time registration
static const auto factory =
vector_factory()
.add("int", int())
.add("double", double())
.add("string", std::string());
int main()
{
auto v = factory.create("int");
auto is = vector_factory().create("int");
auto strs = vector_factory().create("string");
}
Largely based on Jarod42's answer, this is what I will be using:
class base_attribute_vector {};
template<typename T>
class raw_attribute_vector : public base_attribute_vector {
public:
raw_attribute_vector() {std::cout << typeid(T).name() << std::endl; }
};
template<class base, template <typename> class impl>
base* magic(std::string type) {
if(type == "int") return new impl<int>();
else if(type == "float") return new impl<float>();
}
int main() {
auto x = magic<base_attribute_vector, raw_attribute_vector>("int");
auto y = magic<base_attribute_vector, raw_attribute_vector>("float");
}
Short answer: no, you can't instruct the compiler to evaluate a runtime condition in compile time. Not even with hana.
Long answer: there are some (mostly language independent) patterns for this.
I'm assuming that your base_attribute_vector has some virtual method, most likely pure, commonly called an interface in other languages.
Which means that depending on the complexity of your real problem, you probably want a factory or an abstract factory.
You could create a factory or abstract factory without virtual methods in C++, and you could use hana for that. But the question is: is the added complexity really worth it for that (possibly really minor) performance gain?
(also if you want to eliminate every virtual call, even from base_attribute_vector, you have to make everything using that class a template, after the entry point where the switch happens)
I mean, have you implemented this with virtual methods, and measured that the cost of the virtual calls is too significant?
Edit: another, but different solution could be using a variant type with visitors, like eggs::variant.
With variant, you can create classes with functions for each parameter type, and the apply method will switch which function to run based on it's runtime type.
Something like:
struct handler {
void operator()(TypeA const&) { ... }
void operator()(TypeB const&) { ... }
// ...
};
eggs::variant< ... > v;
eggs::variants::apply(handler{}, v);
You can even use templated operators (possibly with enable_if/sfinae), if they have common parts.
Has anyone ever used pointers/references/pointer-to-member (non-type) template parameters?
I'm not aware of any (sane/real-world) scenario in which that C++ feature should be used as a best-practice.
Demonstation of the feature (for pointers):
template <int* Pointer> struct SomeStruct {};
int someGlobal = 5;
SomeStruct<&someGlobal> someStruct; // legal c++ code, what's the use?
Any enlightenment will be much appreciated!
Pointer-to-function:
Pointer-to-member-function and pointer-to-function non-type parameters are really useful for some delegates. It allows you to make really fast delegates.
Ex:
#include <iostream>
struct CallIntDelegate
{
virtual void operator()(int i) const = 0;
};
template<typename O, void (O::*func)(int)>
struct IntCaller : public CallIntDelegate
{
IntCaller(O* obj) : object(obj) {}
void operator()(int i) const
{
// This line can easily optimized by the compiler
// in object->func(i) (= normal function call, not pointer-to-member call)
// Pointer-to-member calls are slower than regular function calls
(object->*func)(i);
}
private:
O* object;
};
void set(const CallIntDelegate& setValue)
{
setValue(42);
}
class test
{
public:
void printAnswer(int i)
{
std::cout << "The answer is " << 2 * i << "\n";
}
};
int main()
{
test obj;
set(IntCaller<test,&test::printAnswer>(&obj));
}
Live example here.
Pointer-to-data:
You can use such non-type parameters to extend the visibility of a variable.
For example, if you were coding a reflexion library (which might very useful for scripting), using a macro to let the user declare his classes for the library, you might want to store all data in a complex structure (which may change over time), and want some handle to use it.
Example:
#include <iostream>
#include <memory>
struct complex_struct
{
void (*doSmth)();
};
struct complex_struct_handle
{
// functions
virtual void doSmth() = 0;
};
template<complex_struct* S>
struct csh_imp : public complex_struct_handle
{
// implement function using S
void doSmth()
{
// Optimization: simple pointer-to-member call,
// instead of:
// retrieve pointer-to-member, then call it.
// And I think it can even be more optimized by the compiler.
S->doSmth();
}
};
class test
{
public:
/* This function is generated by some macros
The static variable is not made at class scope
because the initialization of static class variables
have to be done at namespace scope.
IE:
class blah
{
SOME_MACRO(params)
};
instead of:
class blah
{
SOME_MACRO1(params)
};
SOME_MACRO2(blah,other_params);
The pointer-to-data template parameter allows the variable
to be used outside of the function.
*/
std::auto_ptr<complex_struct_handle> getHandle() const
{
static complex_struct myStruct = { &test::print };
return std::auto_ptr<complex_struct_handle>(new csh_imp<&myStruct>());
}
static void print()
{
std::cout << "print 42!\n";
}
};
int main()
{
test obj;
obj.getHandle()->doSmth();
}
Sorry for the auto_ptr, shared_ptr is available neither on Codepad nor Ideone.
Live example.
The case for a pointer to member is substantially different from pointers to data or references.
Pointer to members as template parameters can be useful if you want to specify a member function to call (or a data member to access) but you don't want to put the objects in a specific hierarchy (otherwise a virtual method is normally enough).
For example:
#include <stdio.h>
struct Button
{
virtual ~Button() {}
virtual void click() = 0;
};
template<class Receiver, void (Receiver::*action)()>
struct GuiButton : Button
{
Receiver *receiver;
GuiButton(Receiver *receiver) : receiver(receiver) { }
void click() { (receiver->*action)(); }
};
// Note that Foo knows nothing about the gui library
struct Foo
{
void Action1() { puts("Action 1\n"); }
};
int main()
{
Foo foo;
Button *btn = new GuiButton<Foo, &Foo::Action1>(&foo);
btn->click();
return 0;
}
Pointers or references to global objects can be useful if you don't want to pay an extra runtime price for the access because the template instantiation will access the specified object using a constant (load-time resolved) address and not an indirect access like it would happen using a regular pointer or reference.
The price to pay is however a new template instantiation for each object and indeed it's hard to think to a real world case in which this could be useful.
The Performance TR has a few example where non-type templates are used to abstract how the hardware is accessed (the hardware stuff starts at page 90; uses of pointers as template arguments are, e.g., on page 113). For example, memory mapped I/O registered would use a fixed pointer to the hardware area. Although I haven't ever used it myself (I only showed Jan Kristofferson how to do it) I'm pretty sure that it is used for development of some embedded devices.
It is common to use pointer template arguments to leverage SFINAE. This is especially useful if you have two similar overloads which you couldn't use std::enable_if default arguments for, as they would cause a redefinition error.
This code would cause a redefinition error:
template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
void foo (T x)
{
cout << "integral";
}
template <typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
void foo (T x)
{
cout << "floating";
}
But this code, which utilises the fact that valid std::enable_if_t constructs collapse to void by default, is fine:
// This will become void* = nullptr
template <typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
void foo (T x)
{
cout << "integral";
}
template <typename T, std::enable_if_t<std::is_floating_point<T>::value>* = nullptr>
void foo (T x)
{
cout << "floating";
}
Occasionally you need to supply a callback function having a particular signature as a function pointer (e.g. void (*)(int)), but the function you want to supply takes different (though compatible) parameters (e.g. double my_callback(double x)), so you can't pass its address directly. In addition, you might want to do some work before and after calling the function.
It's easy enough to write a class template that tucks away the function pointer and then calls it from inside its operator()() or some other member function, but this doesn't provide a way to extract a regular function pointer, since the entity being called still requires the this pointer to find the callback function.
You can solve this problem in an elegant and typesafe way by building an adaptor that, given an input function, produces a customised static member function (which, like a regular function and unlike a non-static member function, can have its address taken and used for a function pointer). A function-pointer template parameter is needed to embed knowledge of the callback function into the static member function. The technique is demonstrated here.
Problem in short:
How could one implement static if functionality, proposed in c++11, in plain c++ ?
History and original problem:
Recently I came up with a problem like this. I need a class Sender with an interface like
class Sender
{
void sendMessage( ... );
void sendRequest( ... );
void sendFile( ... );
// lots of different send methods, not important actually
}
In some cases I will need to create a DoubleSender, i.e. an instance of this class, which would call its methods twice, i.e. when calling, let's say, a sendMessage(...) method, the same message has to be sent twice.
My solutions:
First approach:
Have an isDouble member, and in the end of each method call make a check
sendMessage(...) { ... if( isDouble ) { sendMessage( ... ); }
Well, I don't want this, because actually I will need double posting very recently, and this part of code in time-critical section will be 98% passive.
Second approach:
Inherit a class DoubleSender from Sender, and implement its methods like:
void DoubleSender::sendMessage( ... )
{
Sender::sendMessage(...);
Sender::sendMessage(...);
}
Well, this is acceptable, but takes much space of unpleasant code (really much, because there are lots of different send.. methods.
Third approach:
Imagine that I am using c++11 :). Then I can make this class generic and produce the necessary part of code according to tempalte argument using static if:
enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
void sendMessage(...)
{
// do stuff
static if ( T == Single )
{
sendMessage(...);
}
}
};
This is shorter, easier to read than previous solutions, does not generate additional code and... it's c++11, which I unfortunately cannot use in my work.
So, here is where I came to my question - how can I implement static if analog in c++ ? Also, I would appreciate any other suggestions about how to solve my original problem.
Thanks in advance.
Quoting #JohannesSchaubLitb
with my static_if that works on gcc one can do it :)
in some limited fashion
(see also here)
This trick involves a specific GCC interpretation of the specs on Lambdas in C++11. As such, it will (likely) become a defect report against the standard. This will lead to the trick no longer working in more recent version of GCC (it already doesn't work in 4.7).
See the comment thread below for some more details from Johanness
http://ideone.com/KytVv:
#include <iostream>
namespace detail {
template<bool C>
struct call_if { template<typename F> void operator<<(F) { } };
template<>
struct call_if<true> {
template<typename F>
void operator<<(F f) { f(); }
};
}
#define static_if(cond) detail::call_if<cond>() << [&]
template<bool C, typename T>
void f(T t) {
static_if(C) {
t.foo();
};
}
int main() {
f<false>(42);
}
Why not make the send implementation a policy of the sender class and use CRTP:
template<class Derived>
class SingleSenderPolicy
{
public:
template< class memFunc >
void callWrapperImpl(memFunc f, ...)
{
static_cast<Derived *>(this)->f(...);
}
};
template< class Derived >
class DoubleSenderPolicy
{
public:
template< class memFunc >
void callWrapperImpl(memFunc f, ...)
{
static_cast<Derived *>(this)->f(...);
static_cast<Derived *>(this)->f(...);
}
};
template< class SendPolicy>
class Sender : public SendPolicy< Sender >
{
public:
void sendMessage( ... )
{
// call the policy to do the sending, passing in a member function that
// acutally performs the action
callWrapperImpl( &Sender::sendMessageImpl, ... );
}
void doSomethingElse( ... )
{
callWrapperImpl( &Sender::doSomethingElseImpl, ... );
}
protected:
void sendMessageImpl(... )
{
// Do the sending here
}
void doSomethingElseImpl(... )
{
// Do the sending here
}
};
The public sendXXX functions in you class simply forward to the call wrapper, passing in a member function that implements the real functionality. This member function will be called according to the SendPolicy of the class. CRTP saves the use of bind to wrap the arguments and this pointer up with the member function to call.
With one function it doesn't really cut down on the amount of code, but if you have a lot of calls it could help.
Note: This code is a skeleton to provide a possible solution, it has not been compiled.
Note: Sender<DoubleSenderPolicy> and Sender<SingleSenderPolicy> are completely different types and do not share a dynamic inheritance relationship.
Most compilers do constant folding and dead code removal, so if you write a regular if statement like this:
enum SenderType { Single, Double };
template<SenderType T>
class Sender
{
void sendMessage(...)
{
// do stuff
if ( T == Single )
{
sendMessage(...);
}
}
};
The if branch will get removed when the code is generated.
The need for static if is when the statements would cause a compiler error. So say you had something like this(its somewhat psuedo code):
static if (it == random_access_iterator)
{
it += n;
}
Since you can't call += on non-random access iterators, then the code would always fail to compile with a regular if statement, even with dead code removal. Because the compiler still will check the syntax for before removing the code. When using static if the compiler will skip checking the syntax if the condition is not true.
std::string a("hello world");
// bool a = true;
if(std::is_same<std::string, decltype(a)>::value) {
std::string &la = *(std::string*)&a;
std::cout << "std::string " << la.c_str() << std::endl;
} else {
bool &la = *(bool*)&a;
std::cout << "other type" << std::endl;
}
Suppose I have some per-class data: (AandB.h)
class A
{
public:
static Persister* getPersister();
}
class B
{
public:
static Persister* getPersister();
}
... and lots and lots more classes. And I want to do something like:
persistenceSystem::registerPersistableType( A::getPersister() );
persistenceSystem::registerPersistableType( B::getPersister() );
...
persistenceSystem::registerPersistableType( Z::getPersister() );
... for each class.
My question is: is there a way to automate building a list of per-type data so that I don't have to enumerate each type in a big chunk (as in the above example)?
For example, one way you might do this is: (AutoRegister.h)
struct AutoRegisterBase
{
virtual ~AutoRegisterBase() {}
virtual void registerPersist() = 0;
static AutoRegisterBase*& getHead()
{
static AutoRegisterBase* head= NULL;
return head;
}
AutoRegisterBase* next;
};
template <typename T>
struct AutoRegister : public AutoRegisterBase
{
AutoRegister() { next = getHead(); getHead() = this; }
virtual void registerPersist()
{
persistenceSystem::registerPersistableType( T::getPersister() );
}
};
and use this as follows: (AandB.cxx: )
static AutoRegister<A> auto_a;
static AutoRegister<B> auto_b;
Now, after my program starts, I can safely do: (main.cxx)
int main( int, char ** )
{
AutoRegisterBase* p = getHead();
while ( p )
{
p->registerPersist();
p = p->next;
}
...
}
to collect each piece of per-type data and register them all in a big list somewhere for devious later uses.
The problem with this approach is that requires me to add an AutoRegister object somewhere per type. (i.e. its not very automatic and is easy to forget to do). And what about template classes? What I'd really like is for the instantiation of a template class to somehow cause that class to get automatically registered in the list. If I could do this I would avoid having to have the user of the class (rather than the author) to remember to create a:
static AutoRegister< SomeClass<X1> > auto_X1;
static AutoRegister< SomeClass<X2> > auto_X2;
...
etc....
for each template class instantiation.
For FIW, I suspect there's no solution to this.
You can execute something before main once if a instantiation of a template is made. The trick is to put a static data member into a class template, and reference that from outside. The side effect that static data member triggers can be used to call the register function:
template<typename D>
struct automatic_register {
private:
struct exec_register {
exec_register() {
persistenceSystem::registerPersistableType(
D::getPersister()
);
}
};
// will force instantiation of definition of static member
template<exec_register&> struct ref_it { };
static exec_register register_object;
static ref_it<register_object> referrer;
};
template<typename D> typename automatic_register<D>::exec_register
automatic_register<D>::register_object;
Derive the class you want to be auto-registered from automatic_register<YourClass> . The register function will be called before main, when the declaration of referrer is instantiated (which happens when that class is derived from, which will implicitly instantiate that class from the template).
Having some test program (instead of the register function, a function do_it is called):
struct foo : automatic_register<foo> {
static void do_it() {
std::cout << " doit ";
}
};
int main() {
std::cout << " main ";
}
Yields this output (as expected):
doit main
Register each template at run-time in the constructor. Use a static variable per template to check if the type has already been registered. The following is a quickly hacked together example:
#include <iostream>
#include <vector>
using namespace std;
class Registerable {
static vector<Registerable *> registry_;
public:
static void registerFoo(Registerable *p)
{
registry_.push_back(p);
}
static void printAll()
{
for (vector<Registerable *>::iterator it = registry_.begin();
it != registry_.end(); ++it)
(*it)->print();
}
virtual void print() = 0;
};
vector<Registerable *> Registerable::registry_;
template <typename T>
class Foo : public Registerable {
static bool registered_;
public:
Foo()
{
if (!registered_) {
registerFoo(this);
registered_ = true;
}
}
void print()
{
cout << sizeof (T) << endl;
}
};
template <typename T> bool Foo<T>::registered_ = false;
int
main(int argc, char *argv[])
{
Foo<char> fooChar;
Foo<short> fooShort;
Foo<int> fooInt;
Registerable::printAll();
return 0;
}
It should output the size of each template parameter in the order the classes were instantiated:
1
2
4
This version removes the registration code from each constructor and puts it in a base class.
#include <iostream>
#include <vector>
using namespace std;
class Registerable {
static vector<Registerable *> registry_;
public:
static void registerFoo(Registerable *p)
{
registry_.push_back(p);
}
static void printAll()
{
for (vector<Registerable *>::iterator it = registry_.begin();
it != registry_.end(); ++it)
(*it)->print();
}
virtual void print() = 0;
};
vector<Registerable *> Registerable::registry_;
template <typename T>
class Registerer : public Registerable {
static bool registered_;
public:
Registerer(T *self)
{
if (!registered_) {
registerFoo(self);
registered_ = true;
}
}
};
template <typename T> bool Registerer<T>::registered_ = false;
template <typename T>
class Foo : public Registerer<Foo<T> > {
public:
Foo() : Registerer<Foo<T> >(this) { }
void print()
{
cout << sizeof (T) << endl;
}
};
int
main(int argc, char *argv[])
{
Foo<char> fooChar;
Foo<short> fooShort;
Foo<int> fooInt;
Registerable::printAll();
return 0;
}
I added an example of another non-template class using the registry. So, the final output would be:
foo: 1
foo: 2
foo: 4
bar
The Registerable solution is a neat idea, but has a couple of issues.
Ideally, I'd like to not add code to the constructor:
Because it relies on calling the constructor in order to register the type, it's a
little haphazard about what gets registered and what doesn't.
For things like persistence, I may never call the constructor of a particular type
before using the list, but I may need the type's data in the list in order to
know how to un-persist an object in a file.
There's runtime cost during the constructor call. I'd like to front load the time cost
and not pay the cost many times. If I had a vector of these objects and resized the
vector I'd pay the time-cost each time the copy constructor was called.
Use file-level static blocks to perform the different registrations
A static block? What's that?
A static block is a block of code (i.e. code between curly braces, which defines a scope) that gets executed sometime before main() runs. Java has this feature, and C++ has it too-
Whatch'a talkin' bout, Willis? C++ don't have no static blocks!
No, really, C++ has static blocks. You just need to, uh, shall we say, "expose" their existence.
Hmm. Curious. And how do the static blocks help my registration problem?
It's really very simple. Right after you define class A, you register it like so:
class A { /* ... whatever ... */ };
static_block {
persistenceSystem::registerPersistableType(A::getPersister());
}
There is one caveat, though: static blocks can be a part of the static initialization order fiasco together with any statically-initialized part of your persistence system; so you need to make sure it's ok for these static blocks to run before (most) other statics; and that it's ok for the different classes' Persister's to be registered in arbitrary order.