私は使用しているマイクロコントローラ用の汎用クラスを作成しようとしています。"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.私の "冗長性についてのポイント"を参照)。これは、(ほぼ)同じ(何かをサポートしたいすべてのコントローラでリンス/リピート)私が望む柔軟性です。
また、生成されたアセンブリコードをチェックして、オプティマイザが、特にインラインコードに要求するときに、かなり良い仕事をしていることを確認できます。
を動作することを示したテストプログラムです。たとえば、次のような値を使用することはできません。マクロ内の 'index'。 –
そしてコード内の 'UCSR0B;'は何ですか?それは変数ですか? –
'Uart'をテンプレートとして構文解析する前に前処理が行われ、基本的なテキスト置換だけが実行されます。 – VTT