【问题标题】:Wanted: a C++ template idea to catch an issue, but at compile time?想要:一个 C++ 模板想法来解决问题,但在编译时?
【发布时间】:2010-10-05 12:04:34
【问题描述】:

我们有一个 const 结构数组,如下所示:

static const SettingsSuT _table[] = { {5,1}, {1,2}, {1,1} 等 };

结构如下:

  • size_bytes:
  • num_items:
  • 其他“元数据”成员

所以“总大小”是单个元素的 size_bytes*num_items。所有这些信息都在 const 数组中,在编译时可用。但是,请注意,_table 的总大小与 EEPROM 本身的大小无关。 _table 不镜像 EEPROM,它只描述了布局、使用和其他我们需要的“元数据”类型信息。但是,您可以使用此元数据来确定我们正在使用的 EEPROM 的数量。

该数组仅描述了存储在外部 EEPROM 中的数据,该外部 EEPROM 具有固定/最大大小。随着功能的添加和删除, const 数组中的条目会发生变化。我们目前对数据的总大小进行运行时检查,以确保它不超过 EEPROM 大小。

但是,我们已经将其中许多运行时检查更改为 static_assert 样式的模板检查,以便立即停止构建。我不是模板专家,所以可以在这方面使用一些帮助。

所以,问题是:如何创建一个模板来将所有元素的大小相加(将每个元素的值相乘,然后将所有结果相加)然后执行 static_assert 并在它们超过时停止构建EEPROM 的幻数大小。我将典型的递归阶乘模板示例视为一种方法,但它无法访问数组,它需要一个 const 值(我认为)。

非常感谢您的帮助,

【问题讨论】:

    标签: c++ templates static-assert


    【解决方案1】:

    如果您将值更改为模板参数而不是构造函数参数或其他一些运行时初始化值,那么它们是可用于您的 static_asserts 的常量。

    我不确定这如何适用于结构数组。您可能需要使用一些宏预处理器魔法来声明您的结构,以便它跟踪您的分配。

    BEGIN_EEPROM_STRUCT_TABLE()
      STRUCT(size, num_bytes)
      // etc.
    END_EEPROM_STRUCT_TABLE()
    

    这可能会声明您的表和一个将所有大小相加的 const,前提是它们在编译时是恒定的(当然,您要适当地编写宏)。

    【讨论】:

      【解决方案2】:

      您的问题是它们是常量,但在评估时它们不是常量表达式:

      // f is constant, but its value not known at compile-time
      int const f = rand() % 4;
      

      您需要的是真正的常量表达式。您可以使用boost::mpl 组成一个 mpl 对的 mpl 向量,每个 mpl 对都有一对整数常量:

      using namespace boost::mpl;
      typedef vector<
          pair< int_<5>, int_<1> >,
          pair< int_<1>, int_<2> >,
          pair< int_<1>, int_<1> >,
          > numbers;
      

      现在,您可以使用 boost::mpl 算法迭代其中的项目。每个int_ 都公开一个静态int 常量value,设置为您告诉它的值。这将评估为一个常量表达式:

      // get at the first element of the pair, located in the first element
      // of the vector. Then get its ::value member. 
      int array[at<numbers, 0>::type::first::value];
      

      这实际上会使该数组包含 5 个元素。

      boost::mpl 参考手册网址:Here

      【讨论】:

        【解决方案3】:

        这与 litb 的答案基本相同,但也显示了如何在不对数组大小进行硬编码的情况下将大小相加。这就是它看起来如此复杂的原因。

        不清楚您需要帮助的部分。下面是如何使用类型来跟踪所有元数据而不是内存中的数组。这允许使用枚举进行编译时检查,而您不能在结构中使用 const int 进行检查。如果您有更多元数据,请在设置模板中添加其他参数并将它们存储为枚举值。

        使用 Loki 库:http://loki-lib.sourceforge.net/index.php?n=Main.Development。由于 MakeTypeList 的实现方式,它被限制为 18 个“设置”。

        您可以使用TypeAt&lt;TList, index&gt;::Result::size_bytes(例如)访问元数据。

           #include "loki-0.1.7\include\loki\typelist.h"
           #include "loki-0.1.7\include\loki\static_check.h"
        
           using namespace Loki;
           using namespace Loki::TL;
        
           // based on the Length<> template from loki for adding up the sizes
           template <class TList> struct TotalSize;
           template <> struct TotalSize<NullType>
           { enum { value = 0 }; };
           template <class T, class U>
           struct TotalSize< Typelist<T, U> >
           { enum { value = T::size_bytes*T::num_items + TotalSize<U>::value }; };
        
           // struct for holding the sizes (and other meta data 
           // if you add extra template args and enum values)
           template <size_t s, size_t n> struct Settings
           { enum { size_bytes = s, num_items = n }; };
        
           // the table of setting structs (limited to 18)
           typedef MakeTypelist< Settings<5,1>, Settings<1,2>, Settings<1,1> >
           SettingsSuT;
        
           int _tmain(int argc, _TCHAR* argv[])
           {
               LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value == 8,is8);
               // this will trigger at compile time if uncommented
               //LOKI_STATIC_CHECK(TotalSize< SettingsSuT::Result >::value != 8,isnt8);
        
               int x = TotalSize< SettingsSuT::Result >::value;
               return 0;
           }
        

        【讨论】:

          【解决方案4】:

          在我的扶手椅上,我倾向于尝试将配置数据表示为一个结构,以便可以在编译时检查大小(任何静态断言都可以做到这一点)。根据所提供的信息,很难说这是否是一种合适的方法,但同样,基于同样的理由,我看不出有任何直接的理由来驳回它。

          表格的基本翻译如下:

          struct SettingsTable
          {
              char a[5][1];//maybe call it "featureX_conf", or whatever...
              char b[1][2];
              char c[1][1];
          };
          

          (插入合适的编译器魔法来移除填充和对齐,尽管在实践中char 似乎在大多数平台/编译器组合上通常不受这种事情的影响。)

          然后要获得大小,可以酌情使用sizeof。如果仍然需要实际的值表,则可以通过这种方式生成。它会有点难看,但很容易隐藏在宏后面。

          这是否会比更重模板的方法“更好”可能取决于目标市场。

          【讨论】:

          • 是的,我们在其他表上使用了 sizeof 方法。但是,我想我不是 100% 清楚。这个表更像是一个“元数据”表。它描述了 EEPROM、它的结构以及如何使用每个元素。它不是 EEPROM 的精确“sizeof”副本。谢谢
          猜你喜欢
          • 1970-01-01
          • 2023-03-03
          • 2012-01-25
          • 1970-01-01
          • 2021-06-30
          • 1970-01-01
          • 2020-09-06
          • 2021-01-31
          • 1970-01-01
          相关资源
          最近更新 更多