在编译时生成模板参数

5
我创建了一个类,看起来像一个数组,但它不是将数据保存在程序本身中,而是从文件中流式传输字节(以减少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指令通过汇编器中的一个工具来创建只读文件。
所以我的问题是:我如何消除“location”和“size”变量?我讨厌程序员必须手动输入它们,并且更希望有一种在编译时生成它们的方式。因此,程序员不需要输入:
//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预处理器不会使它变得更丑陋,那么也可以使用。这可能吗?

为了清晰起见进行编辑:

在ReadOnlyBlock类中有这个方法:

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

我无法想出如何打破位置变量和ROM文件之间的内在相互依赖关系。 我确实对工具链有完全控制权,但我需要一种方式来传递汇编器工具如何构建文件以及指示C ++代码文件中块的位置。

另一个编辑:

文件及其块可能非常大,多达1k个字,因此许多预处理器魔法可能会崩溃。 还要感谢所有人到目前为止提供的帮助!


“__asm__” 部分中的 “#location” 真的很棘手。你真的需要它吗?或者你只需要解决其余部分的方案就可以了吗? - Daniel Frey
2
大小很容易确定,但位置需要上下文。模板实例化是一种功能语言,实例化的结果只能根据传递的参数而变化。如果您链接这样的块或在一个大型模板中创建内存,则可以完成此操作。例如,创建一个只读数组的元组,每个数组都有一个位置和大小,从某个位置开始并紧密排列。 - Yakk - Adam Nevraumont
也许我应该包含那个原因。ReadOnlyBlock类中的重载[]运算符调用LoadFromROM(index + location)方法。该工具创建了一个只读文件,其中每个块都位于指定位置,而该类知道如何从该位置加载。我想不出消除这种相互依赖的方法,但我对该工具以及访问代码拥有完全控制权,因此更改它并非不可能。 - Sam Cristall
哦,我刚注意到你在编译时使用了location的字符串值,将其与字符串字面量连接起来,然后将结果传递给了__asm__。 你的__asm__支持constexpr字符串吗? :) - Yakk - Adam Nevraumont
我实际上不太确定。我正在使用带有自定义LLVM后端的Clang 3.0。 - Sam Cristall
4个回答

1

我仍然没有看到生成名称(即#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,因为这似乎已经将数据放入一个坚实的连续块中了。但我有点担心编译器可能会重新排序,这就是我需要位置标识符的地方。在那种情况下,我只需将位置放在数组的第一个索引中,并让工具将其删除。虽然不太美观,但它确实有效!再次感谢! - Sam Cristall

0

这可能会有所帮助。 我写了一些代码来计算块数, 在块声明之前可以添加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;
}

0
如果满足以下条件:
  • 您未在其他地方使用__COUNTER__
  • 所有数组的最大长度为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';
}

在 ideone 上,这将打印出:

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

不幸的是,这些数组大小达到了1k个单词,但我真的喜欢这个解决方案。 我应该澄清一下,抱歉。 我已经编辑了我的帖子。 - Sam Cristall

0

可能有点难以理解,但是使用可变参数模板代替 va_args 是可能的吗?就像这样

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

基本上,任何需要“位置”的地方都使用T1。任何需要“大小”的地方都使用sizeof(TV)+1。没有适当的编译器来测试这个,但也许这是值得考虑的事情...


现在困扰我的不是数据,但还是谢谢你的建议!我可能会稍后将其转换为可变参数模板,但我更习惯于使用C预处理器,只是试图让它正常工作。 - Sam Cristall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接