is back_insert_iterator<> safe to be passed by value? - c++

I have a code that looks something like:
struct Data { int value; };
class A {
public:
typedef std::deque<boost::shared_ptr<Data> > TList;
std::back_insert_iterator<TList> GetInserter()
{
return std::back_inserter(m_List);
}
private:
TList m_List;
};
class AA {
boost::scoped_ptr<A> m_a;
public:
AA() : m_a(new A()) {}
std::back_insert_iterator<A::TList> GetDataInserter()
{
return m_a->GetInserter();
}
};
class B {
template<class OutIt>
CopyInterestingDataTo(OutIt outIt)
{
// loop and check conditions for interesting data
// for every `it` in a Container<Data*>
// create a copy and store it
for( ... it = ..; .. ; ..) if (...) {
*outIt = OutIt::container_type::value_type(new Data(**it));
outIt++; // dummy
}
}
void func()
{
AA aa;
CopyInterestingDataTo(aa.GetDataInserter());
// aa.m_a->m_List is empty!
}
};
The problem is that A::m_List is always empty even after CopyInterestingDataTo() is called. However, if I debug and step into CopyInterestingDataTo(), the iterator does store the supposedly inserted data!
update:
I found the culprit. I actually have something like:
class AA {
boost::scoped_ptr<A> m_a;
std::back_insert_iterator<A::TList> GetDataInserter()
{
//return m_a->GetInserter(); // wrong
return m_A->GetInserter(); // this is the one I actually want
}
// ..... somewhere at the end of the file
boost::scoped_ptr<A> m_A;
};
Now, which answer should I mark as answer?
Really sorry for those not chosen, but you guys definitely got some up-votes : )

The short answer is yes, back_insert_iterator is safe to pass by value. The long answer: From standard 24.4.2/3:
Insert iterators satisfy the
requirements of output iterators.
And 24.1.2/1
A class or a built-in type X satisfies
the requirements of an output iterator
if X is an Assignable type (23.1) ...
And finally from Table 64 in 23.1:
expression t = u
return-type T&
post-condition t is equivalent to u
EDIT: At a glance your code looks OK to me, are you 100% certain that elements are actually being inserted? If you are I would single step through the code and check the address of the aa.m_a->m_List object and compare it to the one stored in outIt in CopyInterestingDataTo, if they're not the same something's fishy.

The following code, which compiles, prints "1", indicating one item added to the list:
#include <iostream>
#include <deque>
#include "boost/shared_ptr.hpp"
#include "boost/scoped_ptr.hpp"
struct Data {
int value;
Data( int n ) : value(n) {}
};
struct A {
typedef std::deque<boost::shared_ptr<Data> > TList;
std::back_insert_iterator<TList> GetInserter()
{
return std::back_inserter(m_List);
}
TList m_List;
};
struct AA {
boost::scoped_ptr<A> m_a;
AA() : m_a(new A()) {}
std::back_insert_iterator<A::TList> GetDataInserter()
{
return m_a->GetInserter();
}
};
struct B {
template<class OutIt>
void CopyInterestingDataTo(OutIt outIt)
{
*outIt = typename OutIt::container_type::value_type(new Data(0));
outIt++; // dummy
}
int func()
{
AA aa;
CopyInterestingDataTo(aa.GetDataInserter());
return aa.m_a->m_List.size();
}
};
int main() {
B b;
int n = b.func();
std::cout << n << std::endl;
}

Related

how to initialize a large size of std::array

i have a class,the class contains a large size of std::array,how to initialize the array??
see class test;
sample:
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test() : /*i need to initialize values at here*/ values_{ctx_,.....}
{
}
protected:
context ctx_;
std::array<value_t,10000> values_;
}
in reality,this array maybe only contains 3 or 5 element,not 10000,but someof people definitely gonna give me an answer like below
test() : values_{ctx_,ctx_,ctx_,ctx_,ctx_}
{
}
i don't need a awkward answer like above.
is there a way to initialize std::array with simple code like fold expression???
You can delegate to a constructor that takes a parameter pack then fold over that:
#include <utility>
#include <cstddef>
class test
{
public:
test() : test(std::make_index_sequence<10000>{}) {}
private:
template<std::size_t... I>
test(std::index_sequence<I...>) : values_{{(I, ctx_)...}} {}
protected:
context ctx_;
std::array<value_t, 10000> values_;
};
Though this absolutely killed compile time at any level of optimisation other than -O0 for me (And will probably blow up your compiled code size)
You could also try constructing into uninitialised memory so you don't need to default construct:
#include <array>
#include <cstddef>
#include <new>
#include <memory>
class test
{
public:
test() {
std::byte* p = value_memory_;
for (std::byte* end = std::end(value_memory_); p < end; p += sizeof(value_t)) {
new (p) value_t(ctx_);
}
}
~test() {
value_t* values = get_values();
std::destroy(values, values + 10000);
}
protected:
context ctx_;
value_t* get_values() {
return std::launder(reinterpret_cast<value_t*>(value_memory_));
}
const value_t* get_values() const {
return std::launder(reinterpret_cast<const value_t*>(value_memory_));
}
// These are UB, but work on most compilers, and would generally be nicer
// to work with
value_t(&get_values())[10000] {
return *std::launder(reinterpret_cast<value_t(*)[10000]>(value_memory_));
}
const value_t(&get_values() const)[10000] {
return *std::launder(reinterpret_cast<const value_t(*)[10000]>(value_memory_));
}
private:
alignas(value_t) std::byte value_memory_[sizeof(value_t) * 10000u];
};
Which will have some runtime cost, and you have to lose the std::array (Unless you go for a std::array<std::aligned_storage_t<sizeof(value_t), alignof(value_t)>, 10000>, in which case you have to launder every single element of the array)
The problem is that your array holds elements of a type that does not have a default constructor, so when you declare a std::array holding that type, the array can only be initialized using aggregate initialization so you can explicitly pass in a value to each element's constructor. When the array is a member of a class or struct, that initialization requires use of the class/struct constructor's member initialization list. Exactly what you are trying to avoid.
To get around this, you can use placement-new to explicitly construct each array element individually in a loop:
#include <type_traits>
class context{......}
class value
{
public:
value(context& ctx) : ctx_(ctx){
}
protected:
context& ctx_;
int data_ = 0;
}
class test
{
public:
test()
{
for (auto &v : values_)
new (&v) value(ctx_);
}
~test()
{
for (auto &v : values_) {
// note: needs std::launder in C++17 and later
// std::launder(reinterpret_cast<value*>(&v))->~value();
reinterpret_cast<value*>(&v)->~value();
}
}
protected:
context ctx_;
using storage_type = std::aligned_storage<sizeof(value), alignof(value)>::type;
std::array<storage_type, 10000> values_;
// Access an object in aligned storage
value& operator[](std::size_t pos)
{
// note: needs std::launder in C++17 and later
// return *std::launder(reinterpret_cast<value*>(&values_[pos]));
return *reinterpret_cast<value*>(&values_[pos]);
}
};
You can use fill() method on the array:
https://en.cppreference.com/w/cpp/container/array/fill

creating type vector in c++

I have several classes that each of them has an ID and the Id is passed to the class as a template parameter:
typedef class1<1> baseClass;
typedef class2<2> baseClass;
typedef class<100> baseClass;
Now I need a map so if I can associate 1 with Class1 and 2 with Class2 and so on.
How can I create such vector? I am working on a header only library, so it should be a header only definition.
I am looking something that do the same thing that this code would do (if someone can compile it!):
std::map<int,Type> getMap()
{
std::map<int,Type> output;
output.add(1,class1);
output.add(2,class2);
output.add(100,class100);
}
The idea is that when I get as input 1, I create a class1 and when I receive 2, I create class2.
Any suggestion is very appreciated.
using this data, then I can write a function like this:
void consume(class1 c)
{
// do something interesting with c
}
void consume(class2 c)
{
// do something interesting with c
}
void consume(class3 c)
{
// do something interesting with c
}
void consume(int id,void * buffer)
{
auto map=getMap();
auto data= new map[id](buffer); // assuming that this line create a class based on map, so the map provide the type that it should be created and then this line create that class and pass buffer to it.
consume(data);
}
As a sketch:
class BaseClass { virtual ~BaseClass() = default; };
template<std::size_t I>
class SubClass : public BaseClass {};
namespace detail {
template<std::size_t I>
std::unique_ptr<BaseClass> makeSubClass() { return { new SubClass<I> }; }
template<std::size_t... Is>
std::vector<std::unique_ptr<BaseClass>(*)> makeFactory(std::index_sequence<Is...>)
{ return { makeSubclass<Is>... }; }
}
std::vector<std::unique_ptr<BaseClass>(*)> factory = detail::makeFactory(std::make_index_sequence<100>{});
We populate the vector by expanding a parameter pack, so we don't have to write out all 100 instantiations by hand. This gives you Subclass<0> at factory[0], Subclass<1> at factory[1], etc. up to Subclass<99> at factory[99].
If I understand correctly you want a map to create different types according to a given number.
If that is so, then the code should look something like this:
class Base
{
};
template <int number>
class Type : public Base
{
public:
Type()
{
std::cout << "type is " << number << std::endl;
}
};
using Type1 = Type<1>;
using Type2 = Type<2>;
using Type3 = Type<3>;
using CreateFunction = std::function<Base*()>;
std::map<int, CreateFunction> creators;
int main()
{
creators[1] = []() -> Base* { return new Type1(); };
creators[2] = []() -> Base* { return new Type2(); };
creators[3] = []() -> Base* { return new Type3(); };
std::vector<Base*> vector;
vector.push_back(creators[1]());
vector.push_back(creators[2]());
vector.push_back(creators[3]());
}
output:
type is 1
type is 2
type is 3
If you need only to create object, it would be enough to implement template creator function like:
template<int ID>
Base<ID> Create()
{
return Base<ID>();
}
And then use it:
auto obj1 = Create<1>();
auto obj2 = Create<2>();
// etc
Working example: https://ideone.com/urh7h6
Due to C++ being a statically-typed language, you may choose to either have arbitrary types that do a fixed set of things or have a fixed set of types do arbitrary things, but not both.
Such limitations is embodied by std::function and std::variant. std::function can have arbitrary types call operator() with a fixed signature, and std::variant can have arbitrary functions visit the fixed set of types.
Since you already said the types may be arbitrary, you may only have a fixed set of things you can do with such a type (e.g. consume). The simplest way is to delegate the hard work to std::function
struct Type
{
template<typename T>
Type(T&& t)
: f{[t = std::forward<T>(t)]() mutable { consume(t); }} {}
std::function<void()> f;
};
void consume(Type& t)
{
t.f();
}
What you are looking for is either the Stategy pattern:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class A {
public:
A() {}
virtual void doIt() {};
};
class Aa : public A {
public:
Aa() {}
virtual void doIt() {
std::cout << "do it the Aa way" << std::endl;
}
};
class Ab : public A {
public:
Ab() {}
virtual void doIt() {
std::cout << "do it the Ab way" << std::endl;
}
};
class Concrete {
public:
Concrete(std::string const& type) {
if (type == ("Aa")) {
_a.reset(new Aa());
} else if (type == "Ab") {
_a.reset(new Ab());
}
}
void doIt () const {
_a->doIt();
}
private:
std::unique_ptr<A> _a;
};
int main() {
std::vector<Concrete> vc;
vc.push_back(Concrete("Aa"));
vc.push_back(Concrete("Ab"));
for (auto const& i : vc) {
i.doIt();
}
return 0;
}
Will output:
do it the Aa way
do it the Ab way

Initializing many private variables in one line

I'm working on legacy code which looks like the following:
class Foo {
public:
Foo();
private:
bool a1, a2, a3 /*, ...*/, a50;
};
Foo::Foo() {
a1 = a2 = a3 /* = ... */ = a50 = false;
}
This is messy. Is there a way to default all private variables of the same time to a single value that's different from the above? I don't want to use an initializer list because there are so many variables.
I know the default constructor of bool assigns false - can this be leveraged?
There are many possible ways to do it, but all of them are very similar. Anyway you will assign each your variable using different forms.
The main method which I think the best is right assign all variables at your constructor line by line. May be its not compact, but it the most meaningful and you allways can easy look your variables default value:
Foo::Foo() {
a1 = false;
a2 = false;
/*...*/
a50 = false;
}
Another method is which you described, with assign operators:
Foo::Foo() {
a1 = a2 = a3 /* = ... */ = a50 = false;
}
And another one allows initialize variables right after constructor declaration:
Foo::Foo() :
a1(false),
a2(false),
/*...*/
a50(true)
{ }
If I forget any method write it to comments, please.
class Foo
{
private:
bool a1{}, a2{}, /*...,*/ a50{};
};
try with this
Foo::Foo (bool aa) : a1 (aa) , a2 (aa), a3 (aa),/*......*/a50(aa){}
You can have another class (in a separate header) which looks like following.
class myBool {
public:
myBool(int x = 1) { _m = x; }
operator bool() const { return 0 < _m; }
private:
int _m;
};
and in your file you can add following
#include "myBool.h"
#define bool myBool
This will initialize all of bool to default value you set in myBool. You may need to add some more methods to myBool class to use it as a full fledge data type. Above is the bare minimum to explain the answer.
Here is an alternative solution to the ones I've seen posted so far, in case it's useful to you.
Put the data you want to mass-initialize to a default false/0 value in its own struct:
struct MyData
{
bool a, b, c, d;
std::string e, f;
};
Now inherit (privately or otherwise) from this struct, and explicitly initialize it in the constructor's initialization list:
class MyClass : private MyData
{
public:
MyClass()
: MyData()
{
}
};
This sets all the bools to false, the strings are empty, any ints become 0, pointers become null, etc, etc
If you forget to put the struct explicitly in the initialization list, some of its members may be uninitialized.
Confirming that it always requires more work to be lazy in c++...
#include <iostream>
#include <utility>
template<class Tuple, std::size_t...Is>
void zero_out_impl(Tuple& t, std::index_sequence<Is...>)
{
using expand = bool[];
(void) expand { false, (std::get<Is>(t) = false)... };
}
template<class...Args>
void zero_out(std::tuple<Args...> t)
{
zero_out_impl(t, std::index_sequence_for<Args...>());
}
struct lots_of_bools {
lots_of_bools()
{
zero_out(std::tie(a,b,c,d,e,f,g,h,i,j));
}
private:
bool a,b,c,d,e,f,g,h,i,j;
};
auto main() -> int
{
lots_of_bools x;
return 0;
}
Here's another way - wrap the bool in a wrapper that default-constructs it.
#include <iostream>
struct auto_false
{
auto_false(bool initial = false) : value(initial) {};
operator bool() const { return value; }
operator bool& () { return value; }
private:
bool value;
};
struct lots_of_bools {
lots_of_bools()
{
}
bool value_of_f() const {
return f;
}
void set_f(bool val) {
f = val;
}
private:
auto_false a,b,c,d,e,f,g,h,i,j;
};
using namespace std;
auto main() -> int
{
lots_of_bools x;
cout << x.value_of_f() << endl;
x.set_f(true);
cout << x.value_of_f() << endl;
return 0;
}
output:
0
1

vector of shared_ptrs, returning it from a function and modifying it

Essentially, I want 2 of my classes to be sharing some data, which i want to have as a vector of shared_ptr objects.
I have cut down my code into the following simple compilable example.
I want object A, to be looking at the data that was initialized in object B. However when I try and do push_back() inside a method of A, it hasnt changed the size of the vector of shared_ptr objects in B. (At the line with the comment "This is the point of interest".)
What approach do I need to use to get this functionality, or am I on the wrong track. (c++ newbie here)
#include <memory>
#include <vector>
#include <iostream>
using std::cout;
using std::endl;
class DataClass {
public:
int i_;
};
class B {
public:
// constructor:
B() : my_data_(std::vector<std::shared_ptr<DataClass> >()) {
my_data_.push_back(std::shared_ptr<DataClass> (new DataClass));
my_data_.push_back(std::shared_ptr<DataClass> (new DataClass));
my_data_[0]->i_ = 1;
my_data_[1]->i_ = 2;
cout<<my_data_.size()<<endl;
};
// return the data
std::vector< std::shared_ptr<DataClass> > get_my_data() {
return my_data_;
};
// check the data:
void CheckData() {
cout<<my_data_.size()<<endl; // This is the point of interest
};
// member variable
std::vector< std::shared_ptr<DataClass> > my_data_;
};
class A {
public:
void start() {
// begin interaction with B class:
B b;
// get the vector of data pointers:
a_has_data_ = b.get_my_data();
// modify some of the data:
a_has_data_.push_back(std::shared_ptr<DataClass> (new DataClass));
a_has_data_[2]->i_ = 42;
b.CheckData();
};
private:
std::vector< std::shared_ptr<DataClass> > a_has_data_;
};
int main() {
A a;
a.start();
}
You are returning a copy of the vector. You need to return a reference to the data:
// return the data
std::vector< std::shared_ptr<DataClass> >& get_my_data()
{
return my_data_;
};
That was A is accessing b's vector, not a copy of it.
a_has_data_.push_back(std::shared_ptr<DataClass> (new DataClass));
With the return type is by copy, the above statement is going to modify the a_has_data_. And in b.CheckData(); you are actually checking the size of b's member.
Introduce a member function in A to check the vector size and you should see the increase.
Here in
std::vector< std::shared_ptr<DataClass> > get_my_data()
The value is got with return by value so new object is created. See the following implementation. It solves your problem by sending pointer.
#include <tr1/memory>
#include <vector>
#include <iostream>
using namespace std;
using std::cout;
using std::endl;
class DataClass {
public:
int i_;
};
class B {
public:
// constructor:
B() : my_data_(std::vector<tr1::shared_ptr<DataClass> >()) {
my_data_.push_back(tr1::shared_ptr<DataClass> (new DataClass));
my_data_.push_back(tr1::shared_ptr<DataClass> (new DataClass));
my_data_[0]->i_ = 1;
my_data_[1]->i_ = 2;
};
// return the data
std::vector< tr1::shared_ptr<DataClass> >* get_my_data() {
return &my_data_;
};
// check the data:
void CheckData() {
cout<<my_data_.size()<<endl; // This is the point of interest
};
// member variable
std::vector< tr1::shared_ptr<DataClass> > my_data_;
};
class A {
public:
void start() {
// begin interaction with B class:
B b;
// get the vector of data pointers:
a_has_data_ = b.get_my_data();
// modify some of the data:
b.CheckData();
a_has_data_->push_back(tr1::shared_ptr<DataClass> (new DataClass));
(*a_has_data_)[2]->i_ = 42;
b.CheckData();
};
private:
std::vector< tr1::shared_ptr<DataClass> >* a_has_data_;
};
int main() {
A a;
a.start();
}
Note: I changed the code to tr1/memory as I have older gcc.

C++ Dynamic Dispatch without Virtual Functions

I've got some legacy code that, instead of virtual functions, uses a kind field to do dynamic dispatch. It looks something like this:
// Base struct shared by all subtypes
// Plain-old data; can't use virtual functions
struct POD
{
int kind;
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
};
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ };
struct Derived1: POD
{
Derived1(): kind(Kind_Derived1) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
struct Derived2: POD
{
Derived2(): kind(Kind_Derived2) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
struct Derived3: POD
{
Derived3(): kind(Kind_Derived3) {}
int GetFoo();
int GetBar();
int GetBaz();
int GetXyzzy();
// ... plus other type-specific data and function members ...
};
// ... and so on for other derived classes ...
and then the POD class's function members are implemented like this:
int POD::GetFoo()
{
// Call kind-specific function
switch (kind)
{
case Kind_Derived1:
{
Derived1 *pDerived1 = static_cast<Derived1*>(this);
return pDerived1->GetFoo();
}
case Kind_Derived2:
{
Derived2 *pDerived2 = static_cast<Derived2*>(this);
return pDerived2->GetFoo();
}
case Kind_Derived3:
{
Derived3 *pDerived3 = static_cast<Derived3*>(this);
return pDerived3->GetFoo();
}
// ... and so on for other derived classes ...
default:
throw UnknownKindException(kind, "GetFoo");
}
}
POD::GetBar(), POD::GetBaz(), POD::GetXyzzy(), and other members are implemented similarly.
This example is simplified. The actual code has about a dozen different subtypes of POD, and a couple dozen methods. New subtypes of POD and new methods are added pretty frequently, and so every time we do that, we have to update all these switch statements.
The typical way to handle this would be to declare the function members virtual in the POD class, but we can't do that because the objects reside in shared memory. There is a lot of code that depends on these structs being plain-old-data, so even if I could figure out some way to have virtual functions in shared-memory objects, I wouldn't want to do that.
So, I'm looking for suggestions as to the best way to clean this up so that all the knowledge of how to call the subtype methods is centralized in one place, rather than scattered among a couple dozen switch statements in a couple dozen functions.
What occurs to me is that I can create some sort of adapter class that wraps a POD and uses templates to minimize the redundancy. But before I start down that path, I'd like to know how others have dealt with this.
You can use a jump table. This is what most virtual dispatches look like under the hood, and you CAN construct it manually.
template<typename T> int get_derived_foo(POD*ptr) {
return static_cast<T>(ptr)->GetFoo();
}
int (*)(POD*) funcs[] = {
get_derived_foo<Derived1>,
get_derived_foo<Derived2>,
get_derived_foo<Derived3>
};
int POD::GetFoo() {
return funcs[kind](this);
}
For a short example.
What exactly are the limitations of being in shared memory? I realized that I don't know enough here. Does it mean that I can't use pointers, because someone in another process will be trying to use those pointers?
You could use a string map, where each process gets it's own copy of the map. You'd have to pass this in to GetFoo() so that it can find it.
struct POD {
int GetFoo(std::map<int, std::function<int()>& ref) {
return ref[kind]();
}
};
Edit: Of course, you don't have to use a string here, you could use an int. I just used it as example. I should change it back. Infact, this solution is pretty flexible, but the important thing is, make a copy of process-specific data, e.g. function pointers or whatever, and then pass it in.
Here's the template-metaprogramming path I'm going down now. Here is what I like about it:
Adding support for a new kind only requires updating LAST_KIND and adding a new KindTraits.
There is a simple pattern for adding a new function.
Functions can be specialized for particular kinds if necessary.
I can expect compile-time errors and warnings, rather than mysterious run-time misbehavior, if I screw anything up.
There are a couple of concerns:
POD's implementation is now dependent upon the interfaces of all the derived classes. (This is already true in the existing implementation, so I'm not worried about it, but it is a bit of a smell.)
I'm counting on the compiler to be smart enough to generate code that is roughly equivalent to the switch-based code.
Many C++ programmers will scratch their heads upon seeing this.
Here's the code:
// Declare first and last kinds
const int FIRST_KIND = Kind_Derived1;
const int LAST_KIND = Kind_Derived3;
// Provide a compile-time mapping from a kind code to a subtype
template <int KIND>
struct KindTraits
{
typedef void Subtype;
};
template <> KindTraits<Kind_Derived1> { typedef Derived1 Subtype; };
template <> KindTraits<Kind_Derived2> { typedef Derived2 Subtype; };
template <> KindTraits<Kind_Derived3> { typedef Derived3 Subtype; };
// If kind matches, then do the appropriate typecast and return result;
// otherwise, try the next kind.
template <int KIND>
int GetFooForKind(POD *pod)
{
if (pod->kind == KIND)
return static_cast<KindTraits<KIND>::Subtype>(pod)->GetFoo();
else
return GetFooForKind<KIND + 1>(); // try the next kind
}
// Specialization for LAST_KIND+1
template <> int GetFooForKind<LAST_KIND + 1>(POD *pod)
{
// kind didn't match anything in FIRST_KIND..LAST_KIND
throw UnknownKindException(kind, "GetFoo");
}
// Now POD's function members can be implemented like this:
int POD::GetFoo()
{
return GetFooForKind<FIRST_KIND>(this);
}
You can experiment with Curiously recurring template pattern. It's a bit complicated, but when you cannot use pure virtual functions it can be helpful.
Here is an approach that uses virtual methods to implement the jump table, without requiring the Pod class or the derived classes to actually have virtual functions.
The objective is to simplify adding and removing methods across many classes.
To add a method, it needs to be added to Pod using a clear and common pattern, a pure virtual function needs to be added to PodInterface, and a forwarding function must be added to PodFuncs using a clear and common pattern.
Derived classes need only have a file static initialisation object to set things up, otherwise look pretty much like they already do.
// Pod header
#include <boost/shared_ptr.hpp>
enum Kind { Kind_Derived1, Kind_Derived2, Kind_Derived3 /* , ... */ };
struct Pod
{
int kind;
int GetFoo();
int GetBar();
int GetBaz();
};
struct PodInterface
{
virtual ~PodInterface();
virtual int GetFoo(Pod* p) const = 0;
virtual int GetBar(Pod* p) const = 0;
virtual int GetBaz(Pod* p) const = 0;
static void
do_init(
boost::shared_ptr<PodInterface const> const& p,
int kind);
};
template<class T> struct PodFuncs : public PodInterface
{
struct Init
{
Init(int kind)
{
boost::shared_ptr<PodInterface> t(new PodFuncs);
PodInterface::do_init(t, kind);
}
};
~PodFuncs() { }
int GetFoo(Pod* p) const { return static_cast<T*>(p)->GetFoo(); }
int GetBar(Pod* p) const { return static_cast<T*>(p)->GetBar(); }
int GetBaz(Pod* p) const { return static_cast<T*>(p)->GetBaz(); }
};
//
// Pod Implementation
//
#include <map>
typedef std::map<int, boost::shared_ptr<PodInterface const> > FuncMap;
static FuncMap& get_funcmap()
{
// Replace with other approach for static initialisation order as appropriate.
static FuncMap s_funcmap;
return s_funcmap;
}
//
// struct Pod methods
//
int Pod::GetFoo()
{
return get_funcmap()[kind]->GetFoo(this);
}
//
// struct PodInterface methods, in same file as s_funcs
//
PodInterface::~PodInterface()
{
}
void
PodInterface::do_init(
boost::shared_ptr<PodInterface const> const& p,
int kind)
{
// Could do checking for duplicates here.
get_funcmap()[kind] = p;
}
//
// Derived1
//
struct Derived1 : Pod
{
Derived1() { kind = Kind_Derived1; }
int GetFoo();
int GetBar();
int GetBaz();
// Whatever else.
};
//
// Derived1 implementation
//
static const PodFuncs<Derived1>::Init s_interface_init(Kind_Derived1);
int Derived1::GetFoo() { /* Implement */ }
int Derived1::GetBar() { /* Implement */ }
int Derived1::GetBaz() { /* Implement */ }
Here is an example using Curiously recurring template pattern. This may suit your needs if you know more info at the compile time.
template<class DerivedType>
struct POD
{
int GetFoo()
{
return static_cast<DerivedType*>(this)->GetFoo();
}
int GetBar()
{
return static_cast<DerivedType*>(this).GetBar();
}
int GetBaz()
{
return static_cast<DerivedType*>(this).GetBaz();
}
int GetXyzzy()
{
return static_cast<DerivedType*>(this).GetXyzzy();
}
};
struct Derived1 : public POD<Derived1>
{
int GetFoo()
{
return 1;
}
//define all implementations
};
struct Derived2 : public POD<Derived2>
{
//define all implementations
};
int main()
{
Derived1 d1;
cout << d1.GetFoo() << endl;
POD<Derived1> *p = new Derived1;
cout << p->GetFoo() << endl;
return 0;
}
Expanding on the solution you ended up with, the following solves the mapping to derived functions at program initialization:
#include <typeinfo>
#include <iostream>
#include <functional>
#include <vector>
enum Kind
{
Kind_First,
Kind_Derived1 = Kind_First,
Kind_Derived2,
Kind_Total
};
struct POD
{
size_t kind;
int GetFoo();
int GetBar();
};
struct VTable
{
std::function<int(POD*)> GetFoo;
std::function<int(POD*)> GetBar;
};
template<int KIND>
struct KindTraits
{
typedef POD KindType;
};
template<int KIND>
void InitRegistry(std::vector<VTable> &t)
{
typedef typename KindTraits<KIND>::KindType KindType;
size_t i = KIND;
t[i].GetFoo = [](POD *p) -> int {
return static_cast<KindType*>(p)->GetFoo();
};
t[i].GetBar = [](POD *p) -> int {
return static_cast<KindType*>(p)->GetBar();
};
InitRegistry<KIND+1>(t);
}
template<>
void InitRegistry<Kind_Total>(std::vector<VTable> &t)
{
}
struct Registry
{
std::vector<VTable> table;
Registry()
{
table.resize(Kind_Total);
InitRegistry<Kind_First>(table);
}
};
Registry reg;
int POD::GetFoo() { return reg.table[kind].GetFoo(this); }
int POD::GetBar() { return reg.table[kind].GetBar(this); }
struct Derived1 : POD
{
Derived1() { kind = Kind_Derived1; }
int GetFoo() { return 0; }
int GetBar() { return 1; }
};
template<> struct KindTraits<Kind_Derived1> { typedef Derived1 KindType; };
struct Derived2 : POD
{
Derived2() { kind = Kind_Derived2; }
int GetFoo() { return 2; }
int GetBar() { return 3; }
};
template<> struct KindTraits<Kind_Derived2> { typedef Derived2 KindType; };
int main()
{
Derived1 d1;
Derived2 d2;
POD *p;
p = static_cast<POD*>(&d1);
std::cout << p->GetFoo() << '\n';
p = static_cast<POD*>(&d2);
std::cout << p->GetBar() << '\n';
}