2017-07-29 14 views
1

私は使用しているマイクロコントローラ用の汎用クラスを作成しようとしています。"const"テンプレート引数で "token pasting operator"を使用できますか?

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

:それはこれらの獣は、新しいトークンを形成するために、その引数を連結するために、フォーム<register prefix> <index> <suffix>でのようなUCSR0B

TCCR1Aだから私が書いたマクロをレジスタを使用することも珍しくありませんここに私はUARTレジスタを使用していますが、それは単なる例です。私はUARTの仕事をしようとはしていません、私はすでにそれを強化したいコードを持っています。

EDIT:好奇心のために、ここでは、そのようなUCSR0Bとしてレジスタの定義はアトメルライブラリごとに、です:

#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) 

今、私は頼ることなく、これらのマクロを使用して動作するように、テンプレートクラスを試してみたいと思いますそれぞれの可能なインデックスのための専門に:uart<0>またはuart<1>またはテンプレート引数のいずれかのunsigned intともちろん

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); } 
}; 

、私はエラーメッセージが表示されます1次のように:マクロ引数は「評価される」ので

error: 'UCSRindexB' was not declared in this scope 

これが私の理解が正しければ、コンパイルされたテンプレート引数の値の前に(私に不適切な用語を使用するための知識をひけらかす恩赦を付与してください)です。とにかくこの仕事をする方法はありますか?


EDITは:Phil's answer 100%が直接私の質問に対処しますが、私は私の心を変更し、別のアプローチを選択しました。私はあなたが冗長にする必要のあるC/C++の言語を見ています。つまり、管理したいすべてのケースを宣言するのが最善です。このような状況では、コーナーを裁断する方が煩雑なコードを生成する可能性があります。これは私の意見ですが、もちろんYMMVです。

duck-typingでハードウェア抽象化を実装しました:私は使用するAtmelプロセッサにさまざまな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モジュールを定義するマイクロコントローラ用の例です:私はまだCを使用してい

// 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 

注意私が上で紹介したこの記事では、レジスタの処理を一般化するために紹介したマクロがあります...読み込みが困難になる傾向がありますが、私は認めます。

次に、テンプレートクラス(実際にはインターフェイスクラス)を作成しました。この引数は、インターフェイスクラスが継承するドライバクラスです。

// 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 

。それは私が1つのアプリケーションをサポートしたいと私はできるだけ多くのプロセッサの比較的一般的なコードを記述することができます:

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

私は、特定のマイクロコントローラに自分のアプリケーションを捧げたい場合は、私も非常に固有のコードを書くことができます。私はアーチ特有のドライバのメンバーを使用する必要があります。

一般的なアプローチでは、すべてのドライバクラスは、そのコンセプトが動作するために特定の数の共通メンバを公開する必要がありますが、新しいマイクロコントローラのサポートを追加するときに一度だけ行う必要があります。これは実際に何らかの反復的な作業です(cf.私の "冗長性についてのポイント"を参照)。これは、(ほぼ)同じ(何かをサポートしたいすべてのコントローラでリンス/リピート)私が望む柔軟性です。

また、生成されたアセンブリコードをチェックして、オプティマイザが、特にインラインコードに要求するときに、かなり良い仕事をしていることを確認できます。

+1

を動作することを示したテストプログラムです。たとえば、次のような値を使用することはできません。マクロ内の 'index'。 –

+0

そしてコード内の 'UCSR0B;'は何ですか?それは変数ですか? –

+1

'Uart'をテンプレートとして構文解析する前に前処理が行われ、基本的なテキスト置換だけが実行されます。 – VTT

答えて

2

次のコードは必要に応じて動作します。マクロが最初に評価されるので、各レジスタに対して手動でテンプレートの特殊化を作成する必要があります。

// 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 
} 
+0

ありがとう!私は、明らかにそれを考えなかった!これは専門分野をセミオートマチックにする良い方法です。私はすきです。 –

関連する問題