【问题标题】:How do you use type traits to do conditional compilation?您如何使用类型特征进行条件编译?
【发布时间】:2012-11-27 01:43:27
【问题描述】:

我正在尝试编写类似 here 的代码,但使用的是 C++11 功能,没有 Boost。

this example 开始工作,我尝试定义一个response_trait,并根据特征的结果进行条件编译。我怎样才能做到这一点?

#include <vector>
using namespace std ;

struct Vector{ float x,y,z ; } ;
struct Vertex { Vector pos ; } ;
struct VertexN { Vector pos, normal ; } ;
struct Matrix {} ;

template <typename T>
struct response_trait {
  static bool const has_normal = false;
} ;

template <>
struct response_trait<VertexN> {
  static bool const has_normal = true;
} ;

template <typename T>
struct Model
{
  vector<T> verts ;

  void transform( Matrix m )
  {
    for( int i = 0 ; i < verts.size() ; i++ )
    {
      #if response_trait<T>::has_normal==true
      puts( "Has normal" ) ;
      // will choke compiler if T doesn't have .normal member
      printf( "normal = %f %f %f\n", verts[i].normal.x, verts[i].normal.y, verts[i].normal.z ) ;
      #else
      puts( "Doesn't have normal" ) ;
      printf( "pos = %f %f %f\n", verts[i].pos.x, verts[i].pos.y, verts[i].pos.z ) ;
      #endif
    }
  }

} ;

int main()
{
  Matrix m ;
  Model<Vertex> model ;
  model.verts.push_back( Vertex() ) ;
  model.transform( m ) ;

  Model<VertexN> modelNormal ;
  modelNormal.verts.push_back( VertexN() ) ;
  modelNormal.transform( m ) ;
}

【问题讨论】:

  • 您能否让您的问题独立并描述您想要实现的目标?
  • 它是独立的。 #if T 有一个.normal 成员,response_trait has_normal 应该为真,并且应该选择正确的编译路径。
  • 除非我完全误解了类型特征。链接的问题是我的出发点,但我不知道我是否采取了错误的方式。
  • 您不能为此使用预处理器指令,因为特征是 C++ 编译时概念,根本不涉及预处理器。
  • 另外,您可以直接将en.cppreference.com/w/cpp/types/is_same 与 VertexN 一起使用。

标签: c++ conditional-compilation typetraits


【解决方案1】:

你可以试试这样的:

void transform_impl(Matrix const & m, std::true_type const &)
{
    // has normal
}

void transform_impl(Matrix const & m, std::false_type const &)
{
    // doesn't have normal
}

template <typename T>
void transform(Matrix const & m)
{
    transform_impl(m, response_trait<T>());
}

你只需要稍微修改一下你的特质:

#include <type_traits>
template <typename> struct response_trait : std::false_type { };
template <> struct response_trait<VertexN> : std::true_type { };

【讨论】:

  • 纯模板专业化有优势吗?
  • @tauran:这普通模板特化,不是吗?我错过了什么吗?还是您指的是重载函数?函数模板不太喜欢特化...
  • @Kerrek SB:我的意思是让 transform 成为一个函数(在Model 之外)而不是一个方法并专门化它。但是现在我发现如果您想保留方法,您的答案是完美的。
  • 只是一个注释,您必须在transform_impl(m, typename response_trait&lt;T&gt;()); 行中取出 out typename 才能在 XCode 4.4、C++11 编译中工作。但代码在 VS2012 中工作。
  • @bobobobo:哦,当然,谢谢。那只是我的一个错误。我已经习惯了写typename trait&lt;T&gt;::type,以至于它有时会溜进来......
【解决方案2】:

如果您的代码可以放入函数中而不会使您的设计繁琐(例如,当您需要访问对象的大量成员变量时),这是一个替代解决方案。当然,有时最好让整个班级都专业化。

#include <vector>
#include <stdio.h>

using namespace std ;

struct Vector{ float x,y,z ; } ;
struct Vertex { Vector pos ; } ;
struct VertexN { Vector pos, normal ; } ;
struct Matrix {} ;

template <typename T>
void printVertex(T vert)
{
      printf( "Doesn't have normal" ) ;
      printf( "pos = %f %f %f\n", vert.pos.x, vert.pos.y, vert.pos.z ) ;
}

template <>
void printVertex(VertexN vert)
{
      printf( "Has normal" ) ;
      printf( "normal = %f %f %f\n", vert.normal.x, vert.normal.y, vert.normal.z ) ;
}

template <typename T>
struct Model
{
  vector<T> verts ;

  void transform( Matrix m )
  {
    for( int i = 0 ; i < verts.size() ; i++ )
    {
        printVertex(verts[i]);
    }
  }
} ;

int main()
{
  Matrix m ;
  Model<Vertex> model ;
  model.verts.push_back( Vertex() ) ;
  model.transform( m ) ;

  Model<VertexN> modelNormal ;
  modelNormal.verts.push_back( VertexN() ) ;
  modelNormal.transform( m ) ;
}

【讨论】:

  • 这很聪明。我没有考虑过制作全局函数而不是成员函数。也许这就是为什么 STL 具有全局函数(std::find 等)的原因
  • 事实证明这种方式有点麻烦,它会鼓励模板特化更多类型而不是使用类型特征。考虑一下你是否有更多的顶点格式,VertexNC(带法线的顶点,颜色),VertexNTC(带法线的顶点,texcoord,颜色)。您必须模板专门化 每个 Vertex 类型,其中包含一个普通类型,而不是在类型特征中设置 hasNormal 标志打开或关闭。
  • 视情况而定。在这种情况下,我也会选择其他解决方案:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-21
  • 1970-01-01
  • 2019-01-30
  • 2017-01-07
  • 1970-01-01
相关资源
最近更新 更多