Question

I am trying to come up with a piece of logic that takes multiple enums and determine a struct type and its corresponding size to pass to a third-party component, and I am considering to use traits to solve this issue in a clean way:

A brute force solution (using one enum) looks something like this:

typedef enum FOO_TYPE
{
    FOO_TYPE_0 = 0,
    FOO_TYPE_1 = 1,
    ...
} FOO_TYPE;

switch (m_foo)
{
    case (FOO_TYPE_0):
    {
        FOO_0 Foo = {};
        UINT size = sizeof(Foo);
        m_thirdParty->CallFunction(&Foo, size);
    }
    break;

    case (FOO_TYPE_1):
    ...
}

I considered using traits to solve this issue by defining specialization for each FOO_TYPE, but hit the issue during compilation because I am picking the specializations of the templates at run-time instead of at compile-time. However, it's not immediately obvious to me how I could resolve this without incurring the cost of the switch statement (which grows very quickly when you consider multiple enum types) above. If anyone has ideas, please let me know.

template <FOO_TYPE FooType> 
struct FooTraits;

template <>
struct FooTraits<FOO_TYPE_0>
{
    typedef FOO_0 FooStruct;
};

{
    typedef FooTraits<m_foo> FooTraitsType; <== THIS USAGE CAUSES COMPILATION ERRORS
    FooTraitsType::FooStruct Foo = {};
    UINT size = sizeof(FooTraitsType::FooStruct);
    m_thirdParty->CallFunction(&Foo, size);
}

Thanks.

Was it helpful?

Solution

You cant do this at run-time using templates, but you do have some other options you can use localise the changes.

My preference would be to use polymorphism:

class Foo {
  static Foo* createFoo(FOO_TYPE type) {
  switch(type) {
     case FOO_TYPE_0:
       return new FooType0();
     case FOO_TYPE_1:
       return new FooType1();
  }
  virtual void callFunction(ThridParty thirdParty)=0;
}

class FooType0 : public Foo {
   void callFunction(ThridParty thirdParty) {
      FOO_0 Foo = {};
      UINT size = sizeof(Foo);
      m_thirdParty->CallFunction(&Foo, size);
   } 
}

class FooType1 : public Foo {
   void callFunction(ThridParty thirdParty) {
      FOO_1 Foo = {};
      UINT size = sizeof(Foo);
      m_thirdParty->CallFunction(&Foo, size);
   } 
}

Your switch block would then become:

// Note this leaks ... see below for non-leaking version.
createFoo(m_foo)->callFunction(m_thirdParty);

Now there's some ugly duplication in each of the FooTypeX classes, so you can remove that with templatization:

class Foo {
  static Foo* createFoo(FOO_TYPE type) {
  switch(type) {
     case FOO_TYPE_0:
       return new FooX<FOO_1>();
     case FOO_TYPE_1:
       return new FooX<FOO_2>();
  }
  virtual void callFunction(ThridParty thirdParty)=0;
}

class FooX<BaseStruct> : public Foo {
   void callFunction(ThridParty thirdParty) {
      BaseStruct foo = {};
      UINT size = sizeof(foo);
      m_thirdParty->CallFunction(&foo, size);
   } 
} 

// A bit of a hack to prevent leaking. In real code you may want to 
// handle ownership differently.
std::unique_ptr<Foo>(createFoo(m_foo))->callFunction(m_thirdParty);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top