Tim*_*imo 6 c++ linker templates
我有一个模板类,它有两个静态成员变量,一个int和另一个std::array<volatile uint_fast32_t, 8>.当我将具有两个不同类(模板本身)的模板实例化为模板参数时,对于其中一个实例,一切都完美无缺,即两个变量只有一个副本.但是,对于另一个,数组在符号表中显示为重复,实际上我的代码有一个错误,当我在一个编译单元中的数组中设置一个值时,更改不会出现在另一个编译单元中.
这是针对嵌入式系统的,这就是使用静态模板进行编译时多态的这种奇怪习惯的原因.
在代码中:标题声明类本身
//dacmux.h
namespace HAL {
template<typename dac_write_sequence_t,
unsigned int chans,
typename sample_t = uint_fast32_t>
struct dacmux {
private:
typedef std::array<volatile sample_t, chans> chans_t;
static chans_t channels;
static unsigned int nextchan;
...
};
//The static variables defined here,
//count on the compiler/linker to make sure
//there is exactly one definition
template<typename dac_write_sequence_t,
unsigned int chans,
typename sample_t>
typename dacmux<dac_write_sequence_t, chans, sample_t>::chans_t dacmux<dac_write_sequence_t, chans, sample_t>::channels{0};
template<typename dac_write_sequence_t, unsigned int chans, typename sample_t>
unsigned int dacmux<dac_write_sequence_t, chans, sample_t>::nextchan = 0;
template<typename dac_t, typename addr_t, typename en_t>
struct muxed_setter {
...
};
template<typename dac_t>
struct dac_setter {
...
};
}//namespace HAL
Run Code Online (Sandbox Code Playgroud)
分配硬件定义的标头:
//Hardware_types.h
...
//Multiplexer for the internal DAC
typedef HAL::dacmux<HAL::muxed_setter<dac1, mux1_addr, mux1_en>, 8> mux1;
//Sequencer for writing the external DAC values
typedef HAL::dacmux<HAL::dac_setter<extdac1>, 8> extdac_sequencer;
...
Run Code Online (Sandbox Code Playgroud)
报头Hardware_types.h被包括在两个源文件,main.cpp,DebugConsole.cpp,这两者同时使用mux1和extdac_sequencer.
至于我理解的,基于这个和许多其他的答案,编译器应该注意每个静态成员变量是否只为模板的每个实例化实例化一次?
但是,当我设置extdac_sequencer::channelsin 的值时DebugConsole.cpp,更改不会反映在声明的中断处理程序中main.cpp.同样适用于mux1::channels.实际上,从符号表中摘录,从.elf中提取objdump -t:
20000280 l O .bss 00000004 _ZN3HAL6dacmuxINS_10dac_setterINS_6ti_dacINS_3SPIINS_5SPI_2EEEN5GPIOs5pin_tINS6_1BELj12EEENS_12_GLOBAL__N_110xx68_frameENSA_12command_xx68ENSA_12channel_xx68EEEEELj8EjE8nextchanE
...
20000254 l O .bss 00000020 _ZN3HAL6dacmuxINS_10dac_setterINS_6ti_dacINS_3SPIINS_5SPI_2EEEN5GPIOs5pin_tINS6_1BELj12EEENS_12_GLOBAL__N_110xx68_frameENSA_12command_xx68ENSA_12channel_xx68EEEEELj8EjE8channelsE
...
20000288 l O .bss 00000020 _ZN3HAL6dacmuxINS_10dac_setterINS_6ti_dacINS_3SPIINS_5SPI_2EEEN5GPIOs5pin_tINS6_1BELj12EEENS_12_GLOBAL__N_110xx68_frameENSA_12command_xx68ENSA_12channel_xx68EEEEELj8EjE8channelsE
...
20000234 w O .bss 00000020 _ZN3HAL6dacmuxINS_12muxed_setterIN4DACs11DAC_channelILj1EN5GPIOs5pin_tINS4_1AELj4EEEEENS4_12bit_stripe_tINS4_1CELj6ELj3EEENS5_ISA_Lj9EEEEELj8EjE8channelsE
...
2000027c w O .bss 00000004 _ZN3HAL6dacmuxINS_12muxed_setterIN4DACs11DAC_channelILj1EN5GPIOs5pin_tINS4_1AELj4EEEEENS4_12bit_stripe_tINS4_1CELj6ELj3EEENS5_ISA_Lj9EEEEELj8EjE8nextchanE
Run Code Online (Sandbox Code Playgroud)
因此,nextchan变量在每次实例化时都会出现一次,因为它应该mux1如此channels.但是,因为extdac_sequencer,channels变量重复,我相信这解释了错误.
我做错了什么,或者这是编译器或链接器错误?
编译:GCC arm-none-eabi 5.2.1 20151202
链接器:arm-none-eabi-ld 2.25.90.20151217
链接器选项: -T mem.ld -T libs.ld -T sections.ld -nostartfiles -Xlinker --gc-sections -L"../ldscripts" -Wl,-Map,"Synth1Firmware.map" -Xlinker --cref --specs=nano.specs
我已经缩小了实现这一目标的条件:
如果第一个模板参数dacmux本身不是模板,那么一切正常,即没有重复的符号:
struct extdac1_setter {
template<typename sample_t>
inline static void update(sample_t val, unsigned int addr) {
extdac1::write_and_update(val, addr);
}
};
//Multiplexer for external DAC, works
typedef HAL::dacmux<extdac1_setter, 8> extdac_sequencer;
Run Code Online (Sandbox Code Playgroud)
但是,如果模板参数本身是模板化的,我会得到重复的符号问题:
template<typename dac_t>
struct dac_setter {
template<typename sample_t>
inline static void update(sample_t val, unsigned int addr) {
dac_t::write_and_update(val, addr);
}
};
//Multiplexer for external DAC, this produces a duplicate symbol
typedef HAL::dacmux<dac_setter<extdac1>, 8> extdac_sequencer;
Run Code Online (Sandbox Code Playgroud)
在这里,extdac1本身也是一个模板:
typedef HAL::DAC8568<dacspi, typename dacspi::nss> extdac1;
Run Code Online (Sandbox Code Playgroud)
...并且dacspi是模板,依此类推.此外,在确实有效的情况下,在另一个瞬间,虽然dac_write_sequence_t是模板,但它不再是模板的模板.所以我开始认为这是模板递归深度的问题,即ld看起来不够深.
另一个有趣的观察:在与具有重复符号完全相同的条件下,Eclipse语法高亮显示器在行声明中说"无效模板参数" extdac_sequencer,尽管实际的编译步骤已经完成.
事实证明这是我的愚蠢:我在定义模板 HAL::DAC8568 的标头中使用了未命名的命名空间,该模板定义为
template<typename spi_t, typename nss_t> using DAC8568 = ti_dac<spi_t, nss_t,
xx68_frame,
command_xx68,
channel_xx68>;
Run Code Online (Sandbox Code Playgroud)
这里xx68_frame、command_xx68、 和channel_xx68都是在未命名的命名空间中定义的(这当然是在标头中做的错误的事情)。这当然意味着,当从不同的编译单元实例化时,我会为每个单元获得不同的类型,因此为 DAC8568 等获得不同的类型,因此获得静态变量的另一个实例是非常自然的。
更改未命名的命名空间以namespace detail立即解决问题。
我仍然对链接器输出中的损坏名称看起来相同这一事实感到有些困惑。怎么可能?
无论如何,我们从中了解到以下内容(其中一些我们已经知道):