How can I go from a runtime value to a template argument?

Background . I am writing several methods designed to be called on thousands of pixels in an image. The image can be in different pixel formats (8-bit grayscale, 16-bit RGB, 24-bit RGB, etc.). It would be inefficient to re-validate and insert into pixel format for every pixel, so I used C ++ templates to generate a version of the whole process for each supported pixel format at compile time:

// Note: PixelFormat::Enum is an integral type.
template<PixelFormat::Enum PixelFormat>
struct EdgeTracer {

  Point FindInitialEdge(const void *pixels, int stride) {
    /* [...] */
  }

  std::vector<Point> TraceEdge(
    const void *pixels, int stride, const Point &initialEdge
  ) {
    /* [...] */
  }

};

      

Now I would like to create a wrapper for these methods as simple C functions (for DLL export to .NET P / Invoke). I am currently doing this:

EXPORT Point DLLAPI FindInitialEdge(
  const void *pixels, int stride, PixelFormat::Enum pixelFormat
) {
  switch(pixelFormat) {
    case PixelFormat::Format8bppIndexed: {
      return EdgeTracer<PixelFormat::Format8bppIndexed>::FindInitialEdge(
        pixels, stride
      );
    }
    case PixelFormat::Format16bppRgb565: {
      return EdgeTracer<PixelFormat::Format16bppRgb565>::FindInitialEdge(
        pixels, stride
      );
    }
    case PixelFormat::Format24bppRgb888: {
      return EdgeTracer<PixelFormat::Format24bppRgb888>::FindInitialEdge(
        pixels, stride
      );
    }
    default: {
      // error handling
    }
  }
}

EXPORT PointBuffer *DLLAPI TraceEdge(
  const void *pixels, int stride, const Point *initialEdge,
  PixelFormat::Enum pixelFormat
) {
  // Nearly the same switch statement all over again
}

      

Less optimal when you need to add support for a new pixel format, you need to touch on many unrelated features. Is there a sane way to avoid redundant operators switch

?

Here is what I have prepared so far, but I have not been able to make a variation version of it:

template<typename T>
void invokeTemplated(
  PixelFormat::Enum pixelFormat, void (T::*method)(const void *pixels)
) {
  switch(pixelFormat) {
    case PixelFormat::Format16bppRgb555: {
      T<PixelFormat::Format16bppRgb555> instance;
      ((&instance)->*method)(pixels);
      break;
    }
    // [...]
  }
}

      

+3


source to share


3 answers


  • write an abstract interface that mirrors your public interface, but only the internal one:

    class Interface {
    public:
      virtual ~Interface() {}
      virtual Point FindInitialEdge(const void *pixels, int stride) = 0;
      // other pure virtual methods here
    };
    
          

  • create a global mapping from pixel type to objects that implement this interface

    typedef std::unordered_map<PixelFormat::Enum, Interface*> ImplMap;
    ImplMap impls;
    
          

  • write all your public functions in the form:

    EXPORT Point DLLAPI FindInitialEdge(const void *pixels, int stride,
                                        PixelFormat::Enum pixelFormat) {
      ImplMap::iterator i = impls.find(pixelFormat);
      if (i != impls.end()) {
        return i->FindInitialEdge(pixels, stride);
      } else {
        // error handling
      }
    }
    
          

  • now you can either inherit template EdgeTracer

    from inherit from Interface

    , or match virtual function definitions, or write template binding. The first is gaining a little less

  • and finally, to register all concrete implementations, you just need one place to do something like

    template <PixelFormat::Enum PF>
    bool register_format() {
      impls[PF] = new EdgeTracer<PF>;
    }
    
    bool initialized = register_format<PixelFormat::Format8bppIndexed>()
                     | register_format<PixelFormat::Format16bppRgb565>()
                     | register_format<PixelFormat::Format24bppRgb888>();
    
          

    just make sure you use the value initialized

    somewhere so it can't be optimized and you have one place to centrally register new formats.



+1


source


Some dirty work around

This is a pretty dirty job, but could you please try:

First:

You need to remind all classes

The definition can do the trick:

# define ALL_MY_CLASS A,B,C //ect....

      

Secondly:

All of your classes should have the same structure:



class A {// same for B, C ... public: static const PixelFormat :: enum e = PixelFormat :: Format8bppIndexed / * associated enum value * /; void methode_a (); // or even yours :: FindInitialEdge};

Then

You must review them.

int test_all_types_for_initial_edge( f) { // here you depack all your possible class
  return (test_types_for_initial_edge<ALL_MY_CLASS>(f));
};

template<typename current, typename second, typename ...elses>
int test_types_for_initial_edge(PixelFormat::enum ee) {
  if (ee == current ::e)//you test if the current tested class  match the enum
   return (EdgeTracer< current >::FindInitalEdge(/* do your stuff */);
 return (test_types_for_initial_edge(ee));
}

template<typename current>
int test_types_for_initial_edge(PixelFormat::enum ee) {
  if (ee == current ::e)//you test if the current tested class  match the enum
   return (EdgeTracer< current >::FindInitalEdge(/* do your stuff */);
 return (DEFAULT_VALUE); //or throw whatever
}

      

What an improvement on this (rather dirty) workaround

I think a little sfinae and a pre-compil check (if the class is like a static_const enum ... or the desired method ... ect ...) might be nice ... But! If you want to update your code, all you have to do is change ALL_MY_CLASS

define!

By the way ...

I don't think to compile completely (which is mostly correct, but part is pseudocode), if you need other help (like adding type_traits and making it a little easier, just ask her ^^)

+1


source


Perhaps the problem is "void * pixels". If you have a specific type for your pixels (8 bit, RGBA, etc.), you can remove the switcher and implement a function for each type.

If you use template functions, the compiler will automatically resolve this type when using a pointer and call the function you want.

0


source







All Articles