【问题标题】:Generating Template Parameters at Compile Time在编译时生成模板参数
【发布时间】:2013-03-11 08:44:46
【问题描述】:

我创建了一个看起来像数组的类,但不是在程序本身中保存数据,而是从文件中流式传输字节(以减少对 RAM 的影响)。现在我已经完成了所有这些工作,但程序员必须使用以下内容定义类:

#define CreateReadOnlyBlock(name, location, size, ...)          \
template<>                                                      \
const unsigned int ReadOnlyBlock<location, size>::Data[]        \
    __asm__( ".readonly__" #location "__" #name)                \
    = { __VA_ARGS__ };                                          \
ReadOnlyBlock<location, size> name;

例子:

//A read only array of {0, 1, 2, 3}
CreateReadOnlyBlock(readOnlyArray, 0, 4, 0, 1, 2, 3); 

请注意,这是针对嵌入式处理器的,asm 指令通过汇编器中的工具来创建只读文件。

所以这是我的问题:如何消除“位置”和“大小”变量?我讨厌程序员必须手动输入它们,并且更喜欢在编译时生成它们的某种方式。所以程序员不需要输入:

//A read only array at location 0 of {0, 1, 2, 3}
CreateReadOnlyBlock(readOnlyArray1, 0, 4, 0, 1, 2, 3); 
//A read only array at location 4 of {4, 5, 6, 7}
CreateReadOnlyBlock(readOnlyArray2, 4, 4, 4, 5, 6, 7); 

他们可以输入:

CreateReadOnlyBlock(readOnlyArray1, 0, 1, 2, 3); 
CreateReadOnlyBlock(readOnlyArray2, 4, 5, 6, 7); 

并且会生成适当的常量。基本上,我正在寻找某种方法来在编译时根据先前的定义生成和放置这些常量。 C ++ 11 是公平的游戏,我只是不太熟悉它(带有 constexpr 的东西似乎是合理的?)。此外,如果 C-Preprocessor 不会使它变得比现在更丑,它也可以。这可能吗?

为清楚起见进行编辑:

ReadOnlyBlock 类中有这个方法:

    template<const int _location, const int _size> class ReadOnlyBlock
    {
        ...
        unsigned int operator[] (size_t index)
        {
            return LoadFromROM(index + _location);
        }
    }

位置变量和 ROM 文件之间存在内在的相互依赖关系,我想不出如何破解。但是,我确实也可以完全控制工具链,但我需要一种方法来传递汇编器工具如何构造文件以及向 C++ 代码指示块所在的位置文件。

另一个编辑:

文件及其块可能相当很大,多达 1k 个字,因此很多预处理器的魔法可能会因此崩溃。另外,感谢大家迄今为止的帮助!

【问题讨论】:

  • #location 中的 __asm__ 部分真的很糟糕。您真的需要这个,还是只对其余的解决方案感到满意?
  • 大小很容易,但位置需要上下文。模板实例化是一种函数式语言,实例化的结果只能根据您传入的内容而有所不同。如果您将这些块链接起来,或者在一个大模板中创建内存,则可以完成。即,创建一个tuple 的只读数组,每个数组都有一个位置和大小,从某个位置开始并打包。
  • 所以也许我应该包括原因。 ReadOnlyBlock 类中重载的 [] 运算符调用 LoadFromROM(index + location);该工具创建一个只读文件,该文件在每个指定位置具有每个块,并且该类知道从该位置加载。我想不出一种方法来消除这种相互依赖,但我确实可以完全控制该工具以及访问代码,因此更改它并不是不可能的。
  • 哦,我刚刚注意到您在编译时使用了location 的字符串值,将包含它的字符串文字连接起来,然后将结果传递给__asm__。您的__asm__ 是否支持constexpr 字符串? :)
  • 我真的不确定。我正在使用带有自定义 LLVM 后端的 Clang 3.0。

标签: c++ c++11 embedded c-preprocessor


【解决方案1】:

如果满足以下条件:

  • 你不要使用__COUNTER__ elswhere
  • 所有数组的最大长度为 5
  • 所有数组都在同一个文件中定义

那么你可以这样做:

#include <iostream>

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

template <int... Args>
struct arg_counter {
  enum { count = sizeof...(Args) };
};

#define INC_COUNTER1 arg_counter<-1>::count
#define INC_COUNTER2 arg_counter<-1, __COUNTER__>::count
#define INC_COUNTER3 arg_counter<-1, __COUNTER__, __COUNTER__>::count
#define INC_COUNTER4 arg_counter<-1, __COUNTER__, __COUNTER__, __COUNTER__>::count
#define INC_COUNTER5 arg_counter<-1, __COUNTER__, __COUNTER__, __COUNTER__, __COUNTER__>::count

#define INC_COUNTER_IMPL2(count, ...) INC_COUNTER ## count
#define INC_COUNTER_IMPL(count, ...) INC_COUNTER_IMPL2(count, __VA_ARGS__) 
#define INC_COUNTER(...) INC_COUNTER_IMPL(VA_NARGS(__VA_ARGS__), __VA_ARGS__)

// removed: __asm__( ".readonly__" #location "__" #name)
#define CreateReadOnlyBlockImpl(name, location, size, ...)      \
  template<>                                                    \
  const unsigned int ReadOnlyBlock<location, size>::Data[]      \
    = { __VA_ARGS__ };                                          \
  ReadOnlyBlock<location, size> name;


#define CreateReadOnlyBlock(name, ...)                                  \
  CreateReadOnlyBlockImpl(name, __COUNTER__, INC_COUNTER(__VA_ARGS__), __VA_ARGS__);

template<int Location, int Size> struct ReadOnlyBlock
{
  static const unsigned int Data[Size];
  int loc () const { return Location; }
  int size() const { return Size; }
};

CreateReadOnlyBlock(readOnlyArray1, 0, 1, 2, 3);
CreateReadOnlyBlock(readOnlyArray2, 4, 5, 6, 7);
CreateReadOnlyBlock(readOnlyArray3, 9);
CreateReadOnlyBlock(readOnlyArray4, 1, 2, 3, 4, 5);

int main()
{
  std::cout << "@" << readOnlyArray1.loc() << ": " << readOnlyArray1.size() << '\n';
  std::cout << "@" << readOnlyArray2.loc() << ": " << readOnlyArray2.size() << '\n';
  std::cout << "@" << readOnlyArray3.loc() << ": " << readOnlyArray3.size() << '\n';
  std::cout << "@" << readOnlyArray4.loc() << ": " << readOnlyArray4.size() << '\n';
}

On ideone this prints:

@0: 4
@4: 4
@8: 1
@9: 5

【讨论】:

  • 不幸的是,数组有 1k 个单词那么大,但我真的很喜欢这个解决方案。我应该澄清一下,对不起。我已经编辑了我的帖子。
【解决方案2】:

我仍然没有看到生成名称的完整解决方案(#location 片段),但对于其余部分,我想你可以使用这样的东西:

template< std::size_t Line >
struct current_location : current_location< Line - 1 > {};

template<>
struct current_location< 0 > : std::integral_constant< std::size_t, 0 > {};

#define NUMARGS(...) (sizeof((int[]){__VA_ARGS__})/sizeof(int))

#define CreateReadOnlyBlock(name, ...)                          \
template<>                                                      \
const unsigned int ReadOnlyBlock<                               \
    current_location<__LINE__-1>::value, NUMARGS(__VA_ARGS__)   \
>::Data[]                                                       \
    __asm__( ".readonly__" #name)                               \
    = { __VA_ARGS__ };                                          \
ReadOnlyBlock<current_location<__LINE__-1>::value,              \
              NUMARGS(__VA_ARGS__)> name;                       \
template<>                                                      \
struct current_location<__LINE__>                               \
    : std::integral_constant<std::size_t,                       \
        current_location<__LINE__-1>::value+NUMARGS(__VA_ARGS__)> \
{};

【讨论】:

  • 这太完美了!太感谢了。我实际上不再相信我需要#location,因为这似乎将数据放入一个可靠的顺序块中。我有点担心编译器可能会重新排序,这是我需要位置标识符的地方。在这种情况下,我只需将该位置作为数组中的第一个索引,然后使用工具将其删除。不漂亮,但嘿,它的工作原理!再次感谢您!
【解决方案3】:

这可能会有所帮助。 我写了一些东西来计算块 可以在块声明之前添加DEF_BLOCK(size)。您可以尝试重写我的示例以在我的块中分配数据

template<size_t ID>
struct block_t
{
   enum{location = 0};
};

#define LAST_BLOCK struct last_block_t{enum{id=__COUNTER__-1};};

#define SPEC2(ID, SIZE) template<> struct block_t<ID>{enum{location = block_t<ID-1>::location + SIZE, prev_block = ID-1, size = SIZE};}
#define SPEC(ID, SIZE) SPEC2(ID, SIZE)

#define DEF_BLOCK(SIZE) SPEC(__COUNTER__, SIZE)

DEF_BLOCK(10);
DEF_BLOCK(11);
LAST_BLOCK;

int _tmain(int argc, _TCHAR* argv[])
{
   std::cout << block_t<last_block_t::id>::location << std::endl;
    return 0;
}

【讨论】:

    【解决方案4】:

    也许掌握了一点,但是可以使用可变参数模板而不是 va_args 吗?类似的东西

    template <typename T1, ... TV>
    class ReadOnlyBlock
    {
        static unsigned int Data[sizeof(TV) + 1];
    };
    

    基本上,任何需要“位置”的地方,都可以使用 T1。在任何需要“大小”的地方,使用 sizeof(TV) + 1。没有合适的编译器来测试它,但也许需要考虑...

    【讨论】:

    • 现在真正要命的不是数据,而是感谢您的建议!稍后我可能会将其转换为可变参数模板,我只是更熟悉 C 预处理器,并试图让它工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-12
    相关资源
    最近更新 更多