背景:我正在编写一些设计用于在图像中的数千个像素上调用的方法。图像可以具有不同的像素格式(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;
}
// [...]
}
}
答案 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)
编写一个镜像公共接口的抽象接口,但只是内部:
class Interface {
public:
virtual ~Interface() {}
virtual Point FindInitialEdge(const void *pixels, int stride) = 0;
// other pure virtual methods here
};
创建从像素类型到实现此接口的对象的全局映射
typedef std::unordered_map<PixelFormat::Enum, Interface*> ImplMap;
ImplMap impls;
以下列形式编写所有公共函数:
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
}
}
现在您可以使EdgeTracer
模板继承自Interface
并匹配虚函数定义,或者编写模板绑定。第一种是打字稍微少一点
最后,要注册所有具体的实现,你只需要一个地方来做类似的事情
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等等),则可以移除开关并为每种类型实现一个功能。
如果使用模板函数,编译器将在您使用指针时自动解析类型,并调用正确的函数。