C++ 在可变模板中使用垫片的更简洁方法?
C++模板通常被等同于bloat的创建者,而Shim的思想正好处理了这一点:使模板只是常规函数上的一个薄包装。这是一个非常好的方法来减少膨胀 例如,让我们使用一个简单的垫片:C++ 在可变模板中使用垫片的更简洁方法?,c++,c++11,variadic-templates,shim,C++,C++11,Variadic Templates,Shim,C++模板通常被等同于bloat的创建者,而Shim的思想正好处理了这一点:使模板只是常规函数上的一个薄包装。这是一个非常好的方法来减少膨胀 例如,让我们使用一个简单的垫片: // // Shim interface // struct Interface { virtual void print(std::ostream& out) const = 0; }; // struct Interface std::ostream& operator<<(std
//
// Shim interface
//
struct Interface {
virtual void print(std::ostream& out) const = 0;
}; // struct Interface
std::ostream& operator<<(std::ostream& out, Interface const& i) {
i.print(out);
return out;
}
template <typename T>
struct IT: public Interface {
IT(T const& t): _t(t) {}
virtual void print(std::ostream& out) const { out << _t; }
T const& _t;
};
template <typename T>
IT<T> shim(T const& t) { return IT<T>(t); }
或类似的
现在,我们如何从原始界面桥接:
template <typename... T>
void printf(char const* format, T const&... t);
模板
void printf(字符常量*格式,T常量和…T);
上面的签名吗?
使用垫片的一个困难是,它们依赖于绑定到const ref行为来延长临时包装器的生命周期,而不必动态分配内存(如果这样做了,它们就不会便宜)
但是,一步完成绑定+数组转换似乎很困难。特别是因为语言中不允许引用数组(以及指向引用的指针)
对于那些感兴趣的人,我有一个解决方案的开始:
//
// printf (or it could be!)
//
void printf_impl(char const*, Interface const** array, size_t size) {
for (size_t i = 0; i != size; ++i) { std::cout << *(array[i]); }
std::cout << "\n";
}
template <typename... T>
void printf_bridge(char const* format, T const&... t) {
Interface const* array[sizeof...(t)] = { (&t)... };
printf_impl(format, array, sizeof...(t));
}
template <typename... T>
void printf(char const* format, T const&... t) {
printf_bridge(format, ((Interface const&)shim(t))...);
}
//
//printf(也可能是!)
//
void printf_impl(字符常量*,接口常量**数组,大小){
对于(size_t i=0;i!=size;++i){std::cout而言,使其轻量化取决于消除类型参数化。如果可以使用同质(内存中大小和对齐方式相同)类型,则您的垫片可能会使用表达式out实例化一些重载类型。请看:
//常规类/方法上的精简模板层
模板
内联void容器::bindSingleAs(){
isMultiBase();//编译时测试
priv::TypeInfoP类型[sizeof…(合同)]
{&typeid(合同)…};
priv::SharedUpcastSignature上传[sizeof…(合同)]
{&priv::shared_upcast…};
//通过非模板方法进行分派。
container->bindSingleAs(&typeid(T)、type、upcast、sizeof…(合同));
}
现在由于评论编辑后,我认为有两个相互冲突的必备条件。
需要一个数组参数吗
不需要复制开销吗
如果printf_impl
函数需要数组作为参数,则这意味着数组元素在内存中应该具有相同的配置(这意味着如果1个元素为64字节,则强制所有其他元素对齐64字节,即使它们为1字节..)因此有必要进行复制,或者至少复制到指向固定位置的指针,因此绝对不可能执行OP想要的操作
我们仍然可以构建该阵列,但我们受到限制:
我们根本不想复制,那么我们应该静态地声明数组的类型,这迫使我们构建第三种类型
auto-Array=MakeArray(/*values*/);
printf(数组);
我们接受复制,所以我们在函数内部构建数组,因为值未知,我们可以对用户隐藏数组,但我们必须将参数复制到固定内存位置,但是我们仍然将数组隐藏在引擎盖下
堆分配,允许在一个非常紧凑的数组中传递参数,但是参数必须驻留在其他位置,堆分配可能代价高昂
第一种解决方案是通过创建一个静态类型的数组来接受额外的编码复杂性,该数组中的元素可以被寻址(所有元素都与最大的元素对齐),但是这是次优的,因为增加对象的大小会影响性能(如果该数组在函数调用之后仍然有效)
第二个解决方案隐藏了模板接口背后的复杂性,但是它无法避免将值临时复制到与第一个解决方案相同的数组的性能成本
所以,很抱歉,这样做是不可能的。另一个答案介于2和3之间。所有其他可能的答案都在这3类中的一类。哦,不,太多了!我的可执行文件中多出几KB就是我的世界末日。@DeadMG这种东西真的可以节省兆字节。@Nawaz没关系,it
必须e在参数到达其构造函数之前就被销毁了。@DeadMG:好吧,如果你愿意的话,我可以用依赖项管理的方式来表述:)能够在.cpp中编写print\u impl
,这意味着我可以避免在头文件中拖拽大量依赖项……这意味着我不仅在最终二进制文件中节省了一些MB,还加快了up编译非常重要。但我想我们并不是都将数千个库作为依赖项来使用;@DarioOO:避免复制相对容易(捕获引用,而不是值);避免动态内存分配更难;Potatoswatter在其答案中管理它(我忘了接受).Theinitializer_list
很好,不幸的是,它也不支持引用,因为内部的typedef。仍然有助于使代码更轻。您认为{(&(Interface const&)shim(t))…}
在printf
中有效吗?ideone错误如下:“错误:请求将
转换为非标量类型std::initializer\u list
”@MatthieuM。它不支持引用,因为它是一个容器并构造了一个数组。我认为没有任何解决方案不涉及数组(或其他容器),因为非模板实现还可以接受什么接口?@MatthieuM。这可能是G++中的一个错误,因为括号中的初始值设定项列表可以转换为初始值设定项列表
专门化。嗯,我没有考虑多态性和容器的问题。是的,尽管这应该是static_cast
,我想这可能是最好的解决方案……不幸的是,我使用的是新系统,尚未重新安装
template <typename... T>
void printf(char const* format, T const&... t);
//
// printf (or it could be!)
//
void printf_impl(char const*, Interface const** array, size_t size) {
for (size_t i = 0; i != size; ++i) { std::cout << *(array[i]); }
std::cout << "\n";
}
template <typename... T>
void printf_bridge(char const* format, T const&... t) {
Interface const* array[sizeof...(t)] = { (&t)... };
printf_impl(format, array, sizeof...(t));
}
template <typename... T>
void printf(char const* format, T const&... t) {
printf_bridge(format, ((Interface const&)shim(t))...);
}
void printf_impl(char const*, std::initializer_list<Interface const*> array) {
for (Interface const* e: list) { std::cout << *e; }
std::cout << "\n";
}
template <typename... T>
void printf_bridge(char const* format, T const&... t) {
printf_impl(format, {(&t)...});
}
template <typename... T>
void printf(char const* format, T const&... t) {
printf_impl(format, { reinterpret_cast< std::intptr_t >( t ) ... } );
}
template <typename... T>
void printf(char const* format, T const&... t) {
printf_impl(format, std::initializer_list< Interface const * >
{ & static_cast< Interface const & >( shim(t) )... } );
}
// thin template layer over regular class/methods
template< typename T, typename... Contracts>
inline void Container::bindSingleAs(){
isMultiBase< T, Contracts...>(); //compile time test
priv::TypeInfoP types[ sizeof...( Contracts)]
{ &typeid( Contracts)... };
priv::SharedUpcastSignature upcasts[ sizeof...( Contracts)]
{ &priv::shared_upcast< T, Contracts>... };
// dispatch over non-template method.
container->bindSingleAs( &typeid(T), types, upcasts, sizeof...( Contracts));
}