【问题标题】:Can I use "token pasting operator" with 'const' template arguments?我可以将“令牌粘贴运算符”与“const”模板参数一起使用吗?
【发布时间】:2017-07-29 15:47:07
【问题描述】:

我正在尝试为我正在使用的微控制器编写一个通用类。这些野兽使用<register prefix> <index> <suffix>形式的寄存器并不罕见,例如UCSR0BTCCR1A

所以我编写了用于连接其参数以形成新标记的宏:

#define uart_is_enabled(i)      (UCSR ## i ## B)
#define uart_putchar(i, c)      UDR ## i = c

注意:这里我使用的是 UART 寄存器,但这只是一个示例,我并不是要让 UART 工作,我已经为它编写了代码,我想对其进行增强。

编辑:出于好奇,这里有一个寄存器的定义,例如 UCSR0B,根据 Atmel 库:

#ifndef __SFR_OFFSET
#  if __AVR_ARCH__ >= 100
#    define __SFR_OFFSET 0x00
#  else
#    define __SFR_OFFSET 0x20
#  endif
#endif

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

#define UCSR0B  _SFR_IO8(0x25)

现在我想尝试使用模板类来处理这些宏,而不需要对每个可能的索引进行专门化:

template <const unsigned index>
class Uart
{
public:
    static bool is_enabled() { return uart_is_enabled(index); }
    static void putchar(uint8_t) { uart_putchar(index, c); }
};

当然,使用uart&lt;0&gt;uart&lt;1&gt; 或任何unsigned int 作为模板参数,我会收到如下错误消息:

 error: 'UCSRindexB' was not declared in this scope

这是因为如果我的理解是正确的,那么在编译模板参数值之前,宏参数会被“评估”(请原谅我使用了不恰当的术语)。有没有办法让它工作?


编辑:虽然Phil's answer 100% 直接解决了我的问题,但我改变了主意并选择了不同的方法。我认为 C/C++ 是一种需要详细说明的语言,即最好声明所有要管理的案例。在某些情况下,像这样,偷工减料可能会产生更繁琐的代码——这只是我的看法,当然,YMMV。

因此,我通过duck-typing 实现了某种形式的硬件抽象:我定义的硬件驱动程序类与我使用的 Atmel 处理器中有许多不同的 UART 一样多——我很幸运没有那么多的 UART 类型以 Atmel 微控制器为型号。

这是一个带有 LIN/UART 驱动程序类的示例:

// lin.h (excerpt)
// Auto-detect the one and only serial interface (set UART mode)

#ifdef LINDAT
/*
 * UART0 driver — for microcontroller which UART module is shared
 * with LIN, e.g. ATmega64M1
 */
struct uart0
{
    static INLINE void power_on() { power_lin_enable(); }
    static INLINE void power_off() { power_lin_disable(); }

    static INLINE void enable() { uart_enable(); }
    static INLINE void disable() { uart_disable(); }

    ...

    static void reset();
};

#endif

以下是定义 1 个或多个 UART 模块的微控制器示例:

// uart.h (excerpt)
// Auto-detect first serial interface aka U[S]ART0

#ifdef UDR0
struct uart0
{
    static INLINE void power_on() { power_usart0_enable(); }
    static INLINE void power_off() { power_usart0_disable(); }

    static INLINE void enable() { uart_enable(0); }
    static INLINE void disable() { uart_disable(0); }

    ...
};

#endif

请注意,我仍在使用我在本文上面介绍的 C 宏来概括寄存器处理...虽然有些往往变得很难阅读,但我承认。

然后我写了一个模板类(实际上是一个接口类),其中参数是驱动类,接口类继承自该类。

// Generic UART wrapper. Comes with a circular input buffer
template <class driver>
class tty : public driver
{
protected:
    ...

public:
    static void putchar(char) { driver::putchar(c); }
    static void power_off()
    {
        driver::disable();
        driver::power_off();
    }
    ...
};

根据检测到的寄存器名称包含控制器特定的头文件:

#if defined(UDR0) || defined(UDR1) || defined(UDR)
#include <drv/uart.h>
#endif

#ifdef LINDAT
#include <drv/lin.h>
#endif

通过这种方法,特定于我正在编译的体系结构的驱动程序成员通过接口类公开。它允许我为单个应用程序支持的尽可能多的处理器编写相对通用的代码:

typedef serio tty<uart0>; // same code for ATmega328p, ATmega64M1, ATtiny1634...
serio::putchar('a');

如果我想将我的应用程序专用于特定的微控制器,我也可以编写非常具体的代码。我只需要使用特定于架构的驱动程序成员。

对于通用方法,按照惯例,我的所有驱动程序类都必须公开一定数量的公共成员才能使该概念起作用,但只需执行一次,即添加对新微控制器的支持时。这确实是某种重复性任务(参见我关于“冗长”的观点),我的意思是(几乎)一遍又一遍地看起来相同的代码(用您想要支持的每个控制器冲洗/重复)但最后这个是我想要的灵活性。

我还检查了生成的汇编代码,我可以确认优化器做得非常好,尤其是我何时何地要求它内联代码。

【问题讨论】:

  • 简短且唯一的答案是“否”。根本不可能使用例如的值index 在宏中。
  • 代码中的UCSR0B; 是什么?是变量吗?
  • 预处理发生在将Uart 解析为模板之前,并且只执行基本的文本替换。
  • 好吧,太糟糕了。我想我将不得不坚持专业化,放弃我的宏,因为在这种情况下它们将毫无用处。感谢您的见解。

标签: c++ templates macros arguments


【解决方案1】:

以下代码可以按需要工作。由于首先评估宏,因此您必须为每个寄存器手动创建模板专业化。

// Simulate register names (to test on any compiler)...
static bool UCSR0B;
static bool UCSR1B;

static char UDR0;
static char UDR1;

与原始帖子相同的宏仍然有用

#define uart_is_enabled(i)      (UCSR ## i ## B)
#define uart_putchar(i, c)      UDR ## i = c

然后我们声明一个模板。这里我使用一个空类。您的默认实现可能不同。

template <const unsigned index>
class Uart
{
};

此时,我使用一个宏来定义所有寄存器的特化。

// Macro that define a template specialization for a specific UART
#define UART(index) \
template <> \
class Uart<index> \
{ \
public: \
    static bool is_enabled() { return uart_is_enabled(index); } \
    static void putchar(uint8_t c) { uart_putchar(index, c); } \
}

// Define Uart<0>, Uart<1>... classes
UART(0);
UART(1);

最后,这是一个测试程序,表明它按预期工作

// Test program
void test()
{
    Uart<0>::is_enabled();
    Uart<1>::is_enabled();
    // Uart<2>::is_enabled();   // Would not compile

    Uart<0>::putchar('a');
    Uart<1>::putchar('b');
    // Uart<2>::putchar('c'); // Would not compile
}

【讨论】:

  • 谢谢!显然,我没有想到这一点!这是使专业化有点像半自动的好方法。我喜欢这样。
猜你喜欢
  • 1970-01-01
  • 2018-04-26
  • 2019-05-26
  • 2018-04-17
  • 2015-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多