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';
}
Related
How can one do this, which is obviously impossible C++, in real C++?:
Type decodeUiEnum(UiEnum myEnum) { // impossible: cannot return a data type
// one switch statement to rule them all
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return GreyscalePixel;
case USER_SELECTED_RGB: return RgbPixel;
...
}
}
void doSomeGraphicsMagic1(UiEnum myEnum) {
...
Foo<decodeUiEnum(myEnum)> a(...); // impossible: type not available at compile
// time
...
}
void doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
...
Bar<int, decodeUiEnum(myEnum)> b(...); // impossible
...
}
and the like, so you can just add new types to the top switch statement and not have to modify the other code below it, so long as that code is suitably generic of course? As otherwise, you would need a switch statement within each function to do the necessary type mapping into the templates, which is not as much maintainable code, and lots of duplication. So more generally - if this is approaching it the wrong way, how do we fulfill that intended property of the code?
That is, what I want to do is, in a function taking an enum as parameter, instantiate a template type where the template parameter depends on the enum, without having a switch-on-enum in every function.
Yes it is actually possible.
Trick is based on partial template specification, this approach used by std::get
For example:
#include <iostream>
// specify an enumeration we will use as type index and related data types
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
struct GreyscalePixel;
struct RgbPixel;
// make base template class
template<UiEnum _EV>
struct ui_enum_type {
};
// do partial type specification trick
// insert typedefs with data type we need for each enumeration value
template<>
struct ui_enum_type<UiEnum::GRAY_SCALE> {
typedef GreyscalePixel pixel_type;
};
template<>
struct ui_enum_type<UiEnum::RGB_PIXEL> {
typedef RgbPixel pixel_type;
};
// demo classes to demonstrate how trick is working at runtime
template<typename T>
struct demo_class {
};
template <>
struct demo_class<GreyscalePixel> {
demo_class()
{
std::cout << "GreyscalePixel" << std::endl;
}
};
template <>
struct demo_class<RgbPixel> {
demo_class()
{
std::cout << "RgbPixel" << std::endl;
}
};
// use swithc trick
static void swich_trick(std::size_t runtimeValue)
{
switch( static_cast<UiEnum>(runtimeValue) ) {
case UiEnum::GRAY_SCALE: {
demo_class< ui_enum_type<UiEnum::GRAY_SCALE>::pixel_type > demo1;
}
break;
case UiEnum::RGB_PIXEL: {
demo_class< ui_enum_type<UiEnum::RGB_PIXEL>::pixel_type > demo2;
}
break;
}
}
int main(int argc, const char** argv)
{
// Do runtime based on the trick, use enum instead of data type
for(std::size_t i=0; i < 2; i++) {
swich_trick(i);
}
return 0;
}
In any case my suggestion - use classic polymorphism instead of template meta-programming over complication. Most modern compilers doing de-virtualization during optimization. For example:
#include <iostream>
#include <memory>
#include <unordered_map>
enum class UiEnum {
GRAY_SCALE,
RGB_PIXEL
};
class GraphicsMagic {
GraphicsMagic(const GraphicsMagic&) = delete;
GraphicsMagic& operator=(const GraphicsMagic&) = delete;
protected:
GraphicsMagic() = default;
public:
virtual ~GraphicsMagic( ) = default;
virtual void doSome() = 0;
};
class GreyscaleGraphicsMagic final: public GraphicsMagic {
public:
GreyscaleGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "GreyscalePixel" << std::endl;
}
};
class RgbGraphicsMagic final: public GraphicsMagic {
public:
RgbGraphicsMagic():
GraphicsMagic()
{
}
virtual void doSome() override
{
std::cout << "RgbPixel" << std::endl;
}
};
int main(int argc, const char** argv)
{
std::unordered_map< UiEnum, std::shared_ptr< GraphicsMagic > > handlers;
handlers.emplace(UiEnum::GRAY_SCALE, new GreyscaleGraphicsMagic() ) ;
handlers.emplace(UiEnum::RGB_PIXEL, new RgbGraphicsMagic() );
for(std::size_t i=0; i < 2; i++) {
handlers.at( static_cast<UiEnum>(i) )->doSome();
}
return 0;
}
You could use std::variant, and then have consuming code std::visit that variant.
First we want a template for "pass a type as a parameter"
template <typename T>
struct tag {
using type = T;
};
Then we define our variant and the factory for it.
using PixelType = std::variant<tag<GreyscalePixel>, tag<RgbPixel>>;
PixelType decodeUiEnum(UiEnum myEnum) {
switch(myEnum) {
case USER_SELECTED_GREYSCALE: return tag<GreyscalePixel>{};
case USER_SELECTED_RGB: return tag<RgbPixel>{};
...
}
}
Now our methods can be written as visitors over PixelType
void doSomeGraphicsMagic1(UiEnum myEnum) {
std::visit([](auto t){
using Pixel = decltype(t)::type;
Foo<Pixel> a(...);
}, decodeUiEnum(myEnum));
}
int doSomeGraphicsMagic2(UiEnum myEnum, int blah) {
return std::visit([blah](auto t){
using Pixel = decltype(t)::type;
Bar<int, Pixel> a(...);
return a.frob();
}, decodeUiEnum(myEnum));
}
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
I'd like to simplify the code I write in my application that handles mutiple data structure types but with a common header. Given something like this:
enum class MyType {
Foo = 100,
Bar = 200,
};
struct Hdr {
MyType type;
};
struct Foo {
Hdr hdr;
int x;
int y;
int z;
};
struct Bar {
Hdr hdr;
double value;
double ratio;
};
void process(const Foo *ptr)
{
// process Foo here
}
void process(const Bar *ptr)
{
// process Bar here
}
extern void *getData();
int main()
{
const void *pv = getData();
auto pHdr = static_cast<const Hdr *>(pv);
switch (pHdr->type) {
case MyType::Foo: process(static_cast<const Foo *>(pv)); break;
case MyType::Bar: process(static_cast<const Bar *>(pv)); break;
default: throw "Unknown";
}
return 0;
}
Ideally I'd like to replace the switch statement above with something like:
process(multi_cast<pHdr->type>(pv);
I'm perfectly okay with having to write statements like this to get it to work:
template<MyType::Foo>
const Foo *multi_cast(void *p)
{
return static_cast<const Foo *>(p);
}
template<MyType::Bar>
const Bar *multi_cast(void *p)
{
return static_cast<const Bar *>(p);
}
But I cannot write a template where the template parameter is a enum (or an int for that matter)
Have I just looked at this for so long that I cannot see an answer?
Or is there just no other way to do it?
There is just no other way to do it.
As the comments have pointed out, since the type is stored in the header at run-time, you have to have some kind of run-time lookup; no amount of templates or overload resolution can help you since all of that is at compile-time.
You can abstract the lookup as much as you want, but you can only replace the switch statement with another type of lookup, and you can only decrease performance the further you get away from a simple switch/lookup table.
For example, you could start with something like this and go nuts:
#include <iostream>
#include <cassert>
enum class Type {
FOO,
BAR,
NUM_
};
struct Header {
Header(Type t)
: type(t)
{}
Type type;
};
struct Foo {
Foo(int x, int y, int z)
: header(Type::FOO), x(x), y(y), z(z)
{}
Header header;
int x;
int y;
int z;
};
struct Bar {
Bar(double value, double ratio)
: header(Type::BAR), value(value), ratio(ratio)
{}
Header header;
double value;
double ratio;
};
static inline void process(Foo*) {
printf("processing foo...\n");
}
static inline void process(Bar*) {
printf("processing bar...\n");
}
using ProcessFunc = void(*)(void*);
static ProcessFunc typeProcessors[(size_t)Type::NUM_] = {
[](void* p) { process((Foo*)p); },
[](void* p) { process((Bar*)p); },
};
static void process(void* p) {
Type t = ((Header*)p)->type;
assert((size_t)t < (size_t)Type::NUM_ && "Invalid Type.");
typeProcessors[(size_t)t](p);
}
static void* get_foo()
{
static Foo foo(0, 0, 0);
return &foo;
}
static void* get_bar()
{
static Bar bar(0.0, 0.0);
return &bar;
}
int main() {
Foo foo(0, 0, 0);
Bar bar(0.0, 0.0);
process(&foo);
process(&bar);
process(get_foo());
process(get_bar());
return 0;
}
but then you're only getting cute and most likely slower. You might as well just put the switch in process(void*)
If you aren't serializing your data(doubtful), are mostly processing one type at a time, and want an OO solution(I wouldn't), you could return a base type that your types inherit from and add a pure virtual process function like so:
struct Type {
virtual void process() = 0;
virtual ~Type() {}
};
struct Foo : Type {
int x = 0;
int y = 0;
int z = 0;
virtual void process() override {
printf("processing foo...\n");
}
};
struct Bar : Type {
double value = 0.0;
double ratio = 0.0;
virtual void process() override {
printf("processing bar...\n");
}
};
static Type* get_foo() {
static Foo foo;
return &foo;
}
static Type* get_bar() {
static Bar bar;
return &bar;
}
int main() {
Foo foo;
Bar bar;
foo.process();
bar.process();
get_foo()->process();
get_bar()->process();
return 0;
}
I would stick with the switch, but I would keep the values of Type::FOO and Type::BAR the default 0 and 1. If you mess with the values too much, the compiler might decide to implement the switch as a bunch of branches as opposed to a lookup table.
You have two issues:
Converting a runtime value (your "type") into a compile time determined type (with associated behavior).
"Unifying" the possible different types to a single (statically at compile time known) type.
Point 2 is what inheritance together with virtual member functions are for:
struct Thing {
virtual void doStuff() const = 0;
virtual ~Thing() {}
};
struct AThing : Thing {
void doStuff() const override { std::cout << "A"; }
};
struct BThing : Thing {
void doStuff() const override { std::cout << "B"; }
};
Point 1 is usually tackled by creating some kind of "factory" mechanism, and then dispatching based on the runtime value to one of those factories. First, the factories:
Thing * factoryA() { return new AThing(); }
Thing * factoryB() { return new BThing(); }
Thing * factory_failure() { throw 42; }
The "dispatching" (or "choosing the right factory") can be done in different ways, one of those being your switch statement (fast, but clumsy), linear search through some container/array (easy, slow) or by lookup in a map (logarithmic - or constant for hashing based maps).
I chose a (ordered) map, but instead of using std::map (or std::unordered_map) I use a (sorted!) std::array to avoid dynamic memory allocation:
// Our "map" is nothing more but an array of key value pairs.
template <
typename Key,
typename Value,
std::size_t Size>
using cmap = std::array<std::pair<Key,Value>, Size>;
// Long type names make code hard to read.
template <
typename First,
typename... Rest>
using cmap_from =
cmap<typename First::first_type,
typename First::second_type,
sizeof...(Rest) + 1u>;
// Helper function to avoid us having to specify the size
template <
typename First,
typename... Rest>
cmap_from<First, Rest...> make_cmap(First && first,
Rest && ... rest) {
return {std::forward<First>(first), std::forward<Rest>(rest)...};
}
Using std::lower_bound I perform a binary search on this sorted array (ehm "map"):
// Binary search for lower bound, check for equality
template <
typename Key,
typename Value,
std::size_t Size>
Value get_from(cmap<Key,Value,Size> const & map,
Key const & key,
Value alternative) {
assert(std::is_sorted(std::begin(map), std::end(map),
[](auto const & lhs, auto const & rhs) {
return lhs.first < rhs.first; }));
auto const lower = std::lower_bound(std::begin(map), std::end(map),
key,
[](auto const & pair, auto k) {
return pair.first < k; });
if (lower->first == key) {
return lower->second;
} else {
// could also throw or whatever other failure mode
return alternative;
}
}
So that, finally, I can use a static const map to get a factory given some runtime value "type" (or choice, as I named it):
int main() {
int const choices[] = {1, 10, 100};
static auto const map =
make_cmap(std::make_pair(1, factoryA),
std::make_pair(10, factoryB));
try {
for (int choice : choices) {
std::cout << "Processing choice " << choice << ": ";
auto const factory = get_from(map, choice, factory_failure);
Thing * thing = factory();
thing->doStuff();
std::cout << std::endl;
delete thing;
}
} catch (int const & value) {
std::cout << "Caught a " << value
<< " ... wow this is evil!" << std::endl;
}
}
(Live on ideone)
The initialization of that "map" could probably made constexpr.
Of course instead of raw pointers (Thing *) you should use managed pointers (like std::unique_ptr). Further, if you don't want to have your processing (doStuff) as member functions, then just make a single "dispatching" (virtual) member function that calls out to a given function object, passing the own instance (this). With a CRTP base class, you don't need to implement that member function for every one of your types.
You're using something that may be called static (=compile-time) polymorphism. This requires to make such switch statements in order to convert the run-time value pHrd->dtype to one of the compile-time values handles in the case clauses. Something like your
process(multi_cast<pHdr->type>(pv);
is impossible, since pHdr->type is not known at compile time.
If you want to avoid the switch, you can use ordinary dynamic polymorphism and forget about the enum Hdr, but use a abstract base class
struct Base {
virtual void process()=0;
virtual ~Base() {}
};
struct Foo : Base { /* ... */ };
struct Bar : Base { /* ... */ };
Base*ptr = getData();
ptr->process();
I am trying to study static polymophism and I implemented the following code. Thanks to the comments from StackOverflow members, I came to understand that what I just wrote is not static polymophism, but actually template-based policy-pattern.
Can anyone give any insight about how to turn this piece of code into static polymophism?
#include <iostream>
template<typename T>
class Interface {
T ex;
public:
double getData() {
return ex.getData(0);
}
};
class Extractor1 {
public:
double getData(const int a) {
return 1;
}
};
class Extractor2 {
public:
double getData(const int a) {
return 2;
}
};
int main() {
// here is the problem: the following 2 variables belong to different types. Therefore, I cannot create an array of pointers which point to the base class
Interface<Extractor1> e1;
Interface<Extractor2> e2;
std::cout<<"FE1 "<< e1.getData() <<" FE2 "<< e2.getData()<<std::endl;
return 0;
}
You can change your code like this to achieve static polymorphism:
#include <iostream>
template <typename T>
class Interface {
public:
double getData(int a) {
return static_cast<T *>(this)->getData(a);
}
};
class Extractor1 : public Interface<Extractor1> {
public:
double getData(int a) {
return 1;
}
};
class Extractor2 : public Interface<Extractor2> {
public:
double getData(int a) {
return 2;
}
};
int main() {
Interface<Extractor1> e1;
Interface<Extractor2> e2;
std::cout << e1.getData(1) << " " << e2.getData(2) << std::endl;
}
The advantage of using static polymorphism is you avoid paying the runtime cost of a vtable lookup like you would when using virtual functions. The drawback though, as I see you are running into based on your 'array' comment, is that you cannot place these different Extractor classes into an array or any other container, because they are both inheriting different base types. The only way around this, aside from using something like a tuple or a container filled with boost::any types, is creating a common base class for your Extractor classes.
I have a set of multiple C++ classes that have the same interface (not derived from each other though). I'm trying to wrap these to make them available in .NET.
I currently have a method that defines the wrapper class using C/C++ #defines and then I can subsequently instantiate classes with a simple line of code
However I can't debug this. Ideally I would like to be able to use a generic or a template. However I can't use a C++ type inside a generic which would be the ultimate way to solve this problem.
Has anyone any idea of how I can do this without using the dreaded macros?
EDIT:
OK Here is an example of the templated class I have written:
template< typename CPPResamplerClass >
ref class TResampler
{
CPPResamplerClass* pResampler;
public:
TResampler( int inputSampleRate, int outputSampleRate, int bufferLen ) :
pResampler( new CPPResamplerClass( inputSampleRate, outputSampleRate, bufferLen ) )
{
}
~TResampler()
{
this->!ResamplerName();
}
!TResampler()
{
if (pResampler)
{
delete pResampler;
pResampler = nullptr;
}
}
property int HistorySize
{
int get()
{
return pResampler->HistorySize();
}
}
array< float >^ ResampleAudio(array< float >^ in)
{
pResampler->Get
array< float >^ out = gcnew array< float >(in->Length);
cli::pin_ptr< float > pIn = &in[0];
cli::pin_ptr< float > pOut = &out[0];
unsigned int inLen = in->Length;
unsigned int outLen = out->Length;
if (pResampler->ResampleAudio(pOut, outLen, pIn, inLen))
{
System::Array::Resize(out, outLen);
return out;
}
return nullptr;
}
};
typedef TResampler< ::Vec::SpeexResample > SpeexResample;
I then want to access this from C# however SpeexResample does not exist. This could well be because I am using a typedef ...
Templates don't exist until they're instantiated. While you could instantiate one explicitly:
template ref class TResampler<SomeNativeClass>;
It wouldn't be exactly user-friendly to use from C#. The exported type will literally have angle brackets in its name. Good luck using that. In C# it's only doable through reflection.
The next best thing is to use derived types. Here's a minimal example:
#include "stdafx.h"
#include <iostream>
namespace CppCli {
class NativeClassA
{
int foo;
public:
NativeClassA(int foo) : foo(foo) { std::cout << "Built native class A" << std::endl; }
int getFoo() const { return foo; }
};
class NativeClassB
{
int foo;
public:
NativeClassB(int foo) : foo(foo) { std::cout << "Built native class B" << std::endl; }
int getFoo() const { return foo; }
};
template<typename NativeClass>
public ref class ManagedWrapper
{
NativeClass* ptr;
public:
ManagedWrapper(int foo)
: ptr(new NativeClass(foo))
{}
~ManagedWrapper()
{
this->!ManagedWrapper();
}
!ManagedWrapper()
{
if (ptr)
{
delete ptr;
ptr = nullptr;
}
}
property int Foo { int get() { return ptr->getFoo(); } }
};
public ref class ManagedWrapperA : ManagedWrapper<NativeClassA>
{
public:
ManagedWrapperA(int foo) : ManagedWrapper(foo) {}
};
public ref class ManagedWrapperB : ManagedWrapper<NativeClassB>
{
public:
ManagedWrapperB(int foo) : ManagedWrapper(foo) {}
};
};
Sure enough, ManagedWrapperA and ManagedWrapperB are visible from C#. Maybe you could macro these definitions and still get a decent debugging experience.