如何从运行时值转换为模板参数?

时间:2014-10-15 13:10:52

标签: c++ templates optimization variadic-functions

背景:我正在编写一些设计用于在图像中的数千个像素上调用的方法。图像可以具有不同的像素格式(8位灰度,16位RGB,24位RGB等)。重新检查和分支每个像素的像素格式是低效的,因此我使用C ++模板在编译时为每个支持的像素格式生成整个过程的版本:

// 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
  ) {
    /* [...] */
  }

};

现在我想为这些方法创建包装器作为普通的C函数(用于DLL导出到.NET P / Invoke)。目前我正在这样做:

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
}

当我想添加对新像素格式的支持时,不必去触摸许多不相关的函数。有没有一种聪明的方法可以避免冗余的switch语句?

这是我到目前为止所做的,但我还没有设法制作它的变量版本:

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 个答案:

答案 0 :(得分:1)

一些肮脏的工作

这是一个非常肮脏的解决方法,但你不能尝试:

第一:

您需要提醒所有课程

定义可以解决这个问题:

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

其次:

您的所有课程都应具有相同的结构:

A级{//对B,C相同... 上市:  static const PixelFormat :: enum e = PixelFormat :: Format8bppIndexed / *关联的枚举值* /;  void methode_a(); //甚至你的:: FindInitialEdge };

之后

你应该浏览beetwen他们

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
}

这种(相当脏的)解决方法的改进是什么

我认为有点sfinae并在预编译时检查(如果类作为static_const枚举...或者想要的方法......等等......)可能相当不错...... 但!如果您想继续更新代码,您只需修改ALL_MY_CLASS define!

即可

顺便说一句......

如果你想要一些其他的帮助(比如添加type_traits并且稍微改善一下这个工作只是问它^^),我认为这完全没有编译(这主要是正确的,但有些部分是伪代码)

答案 1 :(得分:1)

  1. 编写一个镜像公共接口的抽象接口,但只是内部:

    class Interface {
    public:
      virtual ~Interface() {}
      virtual Point FindInitialEdge(const void *pixels, int stride) = 0;
      // other pure virtual methods here
    };
    
  2. 创建从像素类型到实现此接口的对象的全局映射

    typedef std::unordered_map<PixelFormat::Enum, Interface*> ImplMap;
    ImplMap impls;
    
  3. 以下列形式编写所有公共函数:

    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
      }
    }
    
  4. 现在您可以使EdgeTracer模板继承自Interface并匹配虚函数定义,或者编写模板绑定。第一种是打字稍微少一点

  5. 最后,要注册所有具体的实现,你只需要一个地方来做类似的事情

    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>();
    

    请确保您在某处使用initialized的值,以便无法对其进行优化,并且您只需一个地方即可集中注册新格式。

答案 2 :(得分:0)

可能问题出在“void * pixels”中。如果您的像素具有特定类型(8位,RGBA等等),则可以移除开关并为每种类型实现一个功能。

如果使用模板函数,编译器将在您使用指针时自动解析类型,并调用正确的函数。