セクションの複数ページをまとめています。 印刷またはPDF形式で保存...

もとのページに戻る

2024-12-02 現在

関数

グローバル関数

1 - システム関数

時間や乱数を扱う関数

1.1 - millis()

システム時刻の取得
システム時刻[ms]を得ます。
uint32_t millis()

システム時刻はTickTimerの割り込みで更新されます。

1.2 - delay()

待ち処理
ポーリングによる時間待ちを行います。
void delay(uint32_t ms)

msにて与えられた期間待ち処理を行います。

時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。

1.3 - delayMicroseconds()

時間待ち(マイクロ秒)
ポーリングによる時間待ちを行います(μ秒指定)。
void delayMicroseconds(uint32_t microsec)

microsecにて与えられた期間待ち処理を行います。

時間の計測はTickTimerのカウントによって行っています。また長い時間待ちを行う場合はCPUのクロックを低下してポーリング処理を行います。

1.4 - random()

乱数生成
乱数を生成します。
uint32_t random(uint32_t maxval)
uint32_t random(uint32_t minval, uint32_t maxval)

1行目は0..(maxval-1)の値を戻します。maxvalの値が最大値ではないことに注意してください。

2行目はminval..maxval-1の値を戻します。

2 - 汎用デジタルIO

DIOポートを扱う関数

汎用デジタルIO(DIO)の操作には以下の関数を利用します。

  • pinMode()
  • digitalWrite()
  • digitalRead()
  • attachIntDio()
  • detachIntDio()

定数

ピン名と番号

定義名称
const uint8_t PIN_DIGITAL::DIO0 .. 19DIOピン0~19
const uint8_t PIN_DIGITAL::DO0 .. 1DOピン0,1

ピンのモード(DIO0..19)

以下の列挙値は型名 E_PIN_MODEで取り扱われます。

定義プルアップ名称
PIN_MODE::INPUT入力
PIN_MODE::OUTPUT出力
PIN_MODE::INPUT_PULLUP入力
PIN_MODE::OUTPUT_INIT_HIGH出力(初期状態HIGH)
PIN_MODE::OUTPUT_INIT_LOW出力(初期状態LOW)
PIN_MODE::WAKE_FALLING入力、起床ピン、立下り
PIN_MODE::WAKE_RISING入力、起床ピン、立上り
PIN_MODE::WAKE_FALLING_PULLUP入力、起床ピン、立下り
PIN_MODE::WAKE_RISING_PULLUP入力、起床ピン、立上り
PIN_MODE::DISABLE_OUTPUT入力状態に戻す

ピンのモード(DO0,1)

以下の列挙値は型名 E_PIN_MODEで取り扱われます。

定義名称
PIN_MODE::OUTPUT出力
PIN_MODE::OUTPUT_INIT_HIGH出力(初期状態HIGH)
PIN_MODE::OUTPUT_INIT_LOW出力(初期状態LOW)
PIN_MODE::DISABLE_OUTPUT出力設定をやめる

ピンの状態

以下の列挙値は型名 E_PIN_STATEで取り扱われます。

定義名称
PIN_STATE::HIGH1HIGHレベル(=Vccレベル)
PIN_STATE::LOW0LOWレベル(=GNDレベル)

ピンの立ち上がり、立ち下がり

以下の列挙値は型名 E_PIN_INT_MODEで取り扱われます。

定義名称
PIN_INT_MODE::FALLING立ち下り
PIN_INT_MODE::RISING立ち上がり

2.1 - pinMode()

初期化関数
DIO(汎用デジタルIO)ピンの設定を行います。
void pinMode(uint8_t u8pin, E_PIN_MODE mode)

この関数では DIO0..19 と、DO0,1のピンの状態を変更できます。設定内容は E_PIN_MODE の列挙値のDIOの解説DOの解説を参照してください。

2.2 - digitalWrite()

デジタル出力関数
デジタル出力ピンの設定を変更します。
static inline void digitalWrite(uint8_t u8pin, E_PIN_STATE ulVal)

事前にpinMode()にて設定対象のピンを出力用に設定しておきます。1番目のパラメータは、設定対象のピン番号を指定します。2番目のパラメータはHIGHLOWのいずれかを指定します。

2.3 - digitalRead()

デジタル入力関数
入力設定のポートの値を読み出します。
static inline E_PIN_STATE digitalRead(uint8_t u8pin)

事前に入力に設定したピンの入力値をLOWまたはHIGHで得ることができます。

2.4 - attachIntDio()

DIO割り込みハンドラを登録する関数
DIO割り込みハンドラを登録します。
void attachIntDio(uint8_t u8pin, E_PIN_INT_MODE mode)

事前に入力設定したピンに対して、1番目のパラメータは割り込みを有効にしたいピン番号で、2番目は割り込み方向(立ち上がり、立ち下がり)を指定します。

DIO5のピンがHIGHからLOWに変化したときに割り込みが発生する設定を行います。

void setup() {
  the_twelite.app.use<myAppClass>();

  pinMode(PIN_DIGITAL::DIO5, PIN_MODE::INPUT_PULLUP);
  attachIntDio(PIN_DIGITAL::DIO5, PIN_INT_MODE::FALLING);
}

void loop() {
  ;
}

myAppClass.hpp

class myAppClass: public mwx::BrdPal, MWX_APPDEFS_CRTP(myAppClasslMot)
{

};

ビヘイビアmyAppClassの基本定義。詳細は省略しています。

myAppClass.cpp

/*****************************************************************/
// MUST DEFINE CLASS NAME HERE
##define __MWX_APP_CLASS_NAME myAppClass
##include "_mwx_cbs_cpphead.hpp"
/*****************************************************************/

MWX_DIO_INT(PIN_DIGITAL::DIO5, uint32_t arg, uint8_t& handled) {
  static uint8_t ct;
  digitalWrite(PIN_DIGITAL::DIO12, (++ct & 1) ? HIGH : LOW);
	handled = false; // if true, no further event.
}

MWX_DIO_EVENT(PIN_DIGITAL::DIO5, uint32_t arg) {
  Serial << '*';
}

/*****************************************************************/
// common procedure (DO NOT REMOVE)
##include "_mwx_cbs_cpptail.cpp"
// MUST UNDEF CLASS NAME HERE
##undef __MWX_APP_CLASS_NAME
} // mwx
/*****************************************************************/

ビヘイビアmyAppClassの割り込みハンドラの記述。DIO5の割り込み発生時にDIO12の出力設定を反転させ、割り込みハンドラが終了してから発生するイベントではシリアルポートSerial*を表示します。

2.5 - detachIntDio()

DIO割り込みハンドラの登録を解除する関数
割り込みハンドラの登録を解除します。
void detachIntDio(uint8_t u8pin)

2.6 - digitalReadBitmap()

デジタル入力関数(一括)
入力設定のポートの値を一括で読み出します。
uint32_t digitalReadBitmap()

LSB側から順にDIO0 … DIO19 の順に値が格納されます。

HIGH側のピンには 1 が、LOW側のピンには 0 が設定されます。

3 - ユーティリティ関数

その他のユーティリティ関数

3.1 - printfの実装

C言語標準の printf に近い機能
MWX ライブラリでは、C言語で常用されるprintf()に近い実装を用意しています。
int mwx_printf(const char* format, ...)
int mwx_snprintf(char* buffer, size_t count, const char* format, ...)

mwx_printf()Serialオブジェクトに対してprintf出力を行います。Serial.printfmt()と同じ処理になります。

mwx_snprintf()はバッファに対してsnprintfを行います。

3.2 - pack_bits()

指定したビット位置に1をセット
指定したビット位置に1をセットします。
constexpr uint32_t pack_bits(...)

パラメータは可変数引数で指定でき、各パラメータはビット位置を指定する0..31の整数を指定する。例えばpack_bits(1,3,6)と指定すると((1UL<<1)|(1UL<<3)|(1UL<<6))を返します。

背景

IOポート(DI,DO)の状態など各種ビットマップに値を参照・設定する場面があり、その記述を簡素化するため。

3.3 - collect_bits()

指定したビット位置の値からビットマップを作成
整数から指定したビット位置の値を取得し、指定した順番のビットマップを作成します。
constexpr uint32_t collect_bits(uint32_t bm, ...)

パラメータbmに指定する値から、その後の可変数パラメータで指定する0..31のビット位置に対応する値を取り出します。取り出した値はパラメータ順に並べビットマップとして戻り値になります。

ビットマップの並び順は、最初のパラメータを上位ビットとし末尾のパラメータがbit0になります。

uint32_t b1 = 0x12; // (b00010010)
uint32_t b2 = collect_bits(b1, 4, 2, 1, 0);
  // bit4->1, bit2->0, bit1->1, bit0->0
  // b2=0x10 (b1010)

例ではb1のビット4,2,1,0を取り出すと (1,0,1,0) になります。これをb1010として0x10のように計算されます。

背景

IOポート(DI,DO)の状態など各種ビットマップに値を参照・設定する場面があり、その記述を簡素化するため。

3.4 - Byte array utils

バイト配列と16/32ビット整数の相互変換
バイト配列から16/32ビット整数を生成、または16/32ビット整数からバイト配列を生成します。

読み出し

バイト配列から、uint8_t ビッグエンディアン並びとして、uint16_t, uint32_t の値を取得する。

	inline uint8_t G_BYTE(const uint8_t*& p) {
		return *(p)++;
	}
	inline uint16_t G_WORD(const uint8_t*& p) {
		uint32_t r = *p++;
		r = (r << 8) + *p++;
		return r;
	}
	inline uint32_t G_DWORD(const uint8_t*& p) {
		uint32_t r = *p++;
		r = (r << 8) + *p++;
		r = (r << 8) + *p++;
		r = (r << 8) + *p++;
		return r;
	}

p は読み出したバイト数分だけインクリメントされる。

書き込み

ポインタqで指定するバイト配列にuint8_t,ビッグエンディアンでuint16_t,uint32_tの値を書き込む。

	inline uint8_t& S_OCTET(uint8_t*& q, uint8_t c) {
		*q++ = c;
		return *q;
	}
	inline uint8_t& S_WORD(uint8_t*& q, uint16_t c) {
		*(q) = ((c) >> 8) & 0xff; (q)++;
		*(q) = ((c) & 0xff); (q)++;
		return *q;
	}
	inline uint8_t& S_DWORD(uint8_t*& q, uint32_t c) {
		*(q) = ((c) >> 24) & 0xff; (q)++;
		*(q) = ((c) >> 16) & 0xff; (q)++;
		*(q) = ((c) >>  8) & 0xff; (q)++;
		*(q) = ((c) & 0xff); (q)++;
		return *q;
	}

q は書き込んだバイト数分だけインクリメントされる。

背景

無線パケットのデータペイロードの生成・分解時の操作を簡略化するため。

より簡略化したpack_bytes(), expand_bytes()も利用できます。

3.5 - pack_bytes()

要素データを並べてバイト列を生成
要素データを並べてバイト列を生成します。
uint8_t* pack_bytes(uint8_t* b, uint8_t* e, ...)

pack_bytesはコンテナクラスのbegin(),end()イテレータをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。

可変引数パラメータに与えるデータは以下に示すとおりです。

データ型バイト数解説
uint8_t1
uint16_t2ビッグエンディアン並びで格納される
uint32_t4ビッグエンディアン並びで格納される
uint8_t[N]Nuint8_t 型の固定長配列
std::pair<char*,N>Nchar*,uint8_t*型の配列と配列長のペア。make_pair()で生成できる。
smplbuf_u8& pack_bytes(smplbuf_u8& c, ...)

pack_bytesはコンテナオブジェクトをパラメータとし、続くパラメータで指定されるデータをバイト列としてコンテナに書き込みます。コンテナの.push_back()メソッドで末尾に追加します。

可変引数パラメータに与えるデータは以下に示すとおりです。

データ型バイト数解説
uint8_t1
uint16_t2ビッグエンディアン並びで格納される
uint32_t4ビッグエンディアン並びで格納される
uint8_t[N]Nuint8_t 型の固定長配列
std::pair<char*,N>Nchar*,uint8_t*型の配列と配列長のペア。make_pair()で生成できる。
smplbuf_u8?.size()uint8_t型のsmplbuf<>コンテナ。コンテナ長(.size())のデータを格納する。

auto&& rx = the_twelite.receiver.read();

smplbuf<uint8_t, 128> buf;
mwx::pack_bytes(buf
	, uint8_t(rx.get_addr_src_lid())	// src addr (LID)
	, uint8_t(0xCC)							      // cmd id (0xCC, fixed)
	, uint8_t(rx.get_psRxDataApp()->u8Seq)	// seqence number
	, uint32_t(rx.get_addr_src_long())		// src addr (long)
	, uint32_t(rx.get_addr_dst())			// dst addr
	, uint8_t(rx.get_lqi())					  // LQI
	, uint16_t(rx.get_length())				// payload length
	, rx.get_payload() 						    // payload
);

この例では受信パケットの各属性やペイロードを別のバッファbufに再格納しています。

背景

無全パケットのデータペイロードの生成やデータの取り出しで用いられるuint8_t型のバイト配列の記述を簡素化するため。

auto&& rx = the_twelite.receiver.read();

uint8_t data[128];
data[0] = rx.get_addr_src_lid();
data[1] = 0xCC;
data[2] = rx.get_psRxDataApp()->u8Seq;
data[4] = rx.get_addr_src_long() & 0x000000FF;
data[5] = (rx.get_addr_src_long() & 0x0000FF00) >> 8;
data[6] = (rx.get_addr_src_long() & 0x00FF0000) >> 16;
data[7] = (rx.get_addr_src_long() & 0xFF000000) >> 24;
...

上記はもっとも単純な記述だが、以下のようにByte array utilsを用いてバイト配列を生成できる。

auto&& rx = the_twelite.receiver.read();

uint8_t data[128], *q = data;
S_OCTET(q, rx.get_addr_src_lid());
S_OCTET(q, 0xCC);
S_OCTET(q, rx.get_psRxDataApp()->u8Seq);
S_DWORD(q, rx.get_addr_src_long());
S_DWORD(q, rx.get_addr_dst());
S_OCTET(q, rx.get_lqi());
S_WORD(q, rx.get_length());
for (auto x : rx.get_payload()) {
  S_OCTET(q, x);
}

3.6 - expand-bytes()

“バイト列を分解し変数に格納”
バイト列を分解し変数に格納します。
const uint8_t* expand_bytes(
        const uint8_t* b, const uint8_t* e, ...)

expand_bytes()は、パラメータにuint8_t*型のイテレータの組み合わせを指定します。これは解析対象の先頭と末尾の次のイテレータの指定となります。eの位置まで解析が進んだ場合はエラーとなりnullptrを返します。

展開にエラーがない場合は、次の読み出しを行うイテレータを戻します。

可変数パラメータには以下を指定します。

バイト数データ長解説
uint8_t1
uint16_t2ビッグエンディアン並びとして展開する
uint32_t4ビッグエンディアン並びとして展開する
uint8_t[N]Nuint8_t 型の固定長配列
std::pair<char*,N>Nchar*,uint8_t*型の配列と配列長Nのペアmake_pair()で生成できる

auto&& rx = the_twelite.receiver.read();

char fourchars[5]{};
auto&& np =
	expand_bytes(rx.get_payload().begin(), rx.get_payload().end()
		, make_pair((uint8_t*)fourchars, 4)
    );

// read rest of payload
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];
expand_bytes(np, rx.get_payload().end()
	, u8DI_BM_remote
	, au16AI_remote[0]
	, au16AI_remote[1]
	, au16AI_remote[2]
	, au16AI_remote[3]
	, au16AI_remote[4]
);

この例では、まず4バイトの文字列を読み出しています。ここではmake_pair()を用いて明示的に4バイト分のデータを読み出します。

戻されたイテレータnpをもとに、次のデータを読み出します。次のデータはuint8_t型、あとはuint16_t型が5つ続いています。

背景

無全パケットのデータペイロードの生成やデータの取り出しで用いられるuint8_t型のバイト配列の記述を簡素化するため。

auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];

uint8_t *p = rx.get_payload().begin();
fourchars[0] = p[0];
fourchars[1] = p[1];
fourchars[2] = p[2];
fourchars[3] = p[3];
fourchars[4] = 0;
p += 4;

u8DI_BM_remote = (p[0] << 8) + p[1]; p+=2;
au16AI_remote[0] = (p[0] << 8) + p[1]; p+=2;
...

上記はもっとも単純な記述だが、以下のようにByte array utilsを用いてバイト配列から読み出せる。

auto&& rx = the_twelite.receiver.read();
char fourchars[5]{};
uint8_t u8DI_BM_remote = 0xff;
uint16_t au16AI_remote[5];

uint8_t *p = rx.get_payload().begin();
fourchars[0] = G_BYTE(p);
fourchars[1] = G_BYTE(p);
fourchars[2] = G_BYTE(p);
fourchars[3] = G_BYTE(p);
fourchars[4] = 0;

u8DI_BM_remote = G_WORD(p);
au16AI_remote[0] = G_WORD(p);
...

3.7 - CRC8, XOR, LRC

チェックサム計算
チェックサムの計算で良く用いられる値を計算します。
uint8_t CRC8_u8Calc(uint8_t *pu8Data, uint8_t size, uint8_t init=0)
uint8_t CRC8_u8CalcU32(uint32_t u32c, uint8_t init=0)
uint8_t CRC8_u8CalcU16(uint16_t u16c, uint8_t init=0)
uint8_t XOR_u8Calc(uint8_t *pu8Data, uint8_t size)
uint8_t LRC_u8Calc(uint8_t* pu8Data, uint8_t size)

CRC8, XOR, LRC(アスキー形式で使用)の計算を行います。

CRC8_u8CalcU16(), CRC8_u8CalcU32()u16c, u32cをビッグエンディアン並びとして、CRC8を計算します。

背景

無線パケットのデータ列、アスキー形式のチェックサム(LRC)、各種センサーのデータチェック用に利用されるため、ライブラリ手続きとして追加した。

3.8 - div10(), div100(), div1000()

10, 100 または 1000 で割った商と剰余を計算
10, 100 または 1000 で割った商と剰余を計算します。
struct div_result_i32 {
		int32_t quo; // quotient
		int16_t rem; // remainder
		uint8_t b_neg;  // true if negative
		uint8_t digits_rem; // digits of remainder
};

div_result_i32 div10(int32_t val);
div_result_i32 div100(int32_t val);
div_result_i32 div1000(int32_t val);

センサー値などで100倍した値をuint16_t型にして受け渡しする場合がありますが、除算回路がないマイコンでの計算処理には相応の時間がかかるため、加算・減算・乗算とビットシフトを用いた近似計算と補正により計算を行います。

valに計算したい値、remは余りの格納変数、negは符号を格納する変数を渡します。

戻り値は商の値(常に正)、remには余りの値(常に正)、negは負ならtrueが格納されます。

計算アルゴリズムの制約(桁あふれ)からdiv100()div1000()での計算可能な値域が決まっています。div100()は-99999~99999までの値に対応し、div1000()は-999999~999999までの値に対応します。

使用例

auto d1 = div100(sns_val.u16temp_object);
auto d2 = div100(sns_val.u16temp_object);

Serial
	<< crlf << format("..Object  = %c%2d.%02d"
									, d1.b_neg ? '-' : '+', d1.quo, d1.rem)
	        << format("  Ambient = %c%2d.%02d"
									, d2.b_neg ? '-' : '+', d2.quo, d2.rem);

計算速度

10分の1程度になります。

結果の出力

// 変換オプション
struct DIVFMT {
  static const int STD = 0; // displays with minimul digits (no padding, no positive sign)
  static const int PAD_ZERO = 1; // set padding character as '0' instead of ' '.
  static const int SIGN_PLUS = 2; // put '+' sign if value is positive or 0.
  static const int PAD_ZERO_SIGN_PLUS = 3; // PAD_ZERO & SIGN_PLUS
  static const int SIGN_SPACE = 4; // put ' ' sign if value is positive or 0.
  static const int PAD_ZERO_SIGN_SPACE = 5; // PAD_ZERO & SIGN_SPACE
};

// 文字列変換結果を格納するためのクラス
class _div_chars {
  ...
  const char* begin() const {...}
  const char* end() const {...}
  const char* c_str() const { return begin(); }
  operator const char*() const { return begin(); }
};

// format()メソッド
_div_chars div_result_i32::format(
    int dig_quo = 0, uint32_t opt = DIVFMT::STD) const;

// Serialへのインタフェースの実装
template <class D> class stream {
...
		inline D& operator << (const mwx::_div_chars&& divc);
		inline D& operator << (const mwx::div_result_i32&&);
		inline D& operator << (const mwx::div_result_i32&);
};

割り算の結果を格納するdiv_result_i32クラスにはformat()メソッドを用い_div_charsクラスオブジェクトを得ることが出来る。_div_chars()クラスオブジェクトは文字列バッファを内包していてconst char*型として文字列バッファにアクセスするメソッドが用意されている。また、Serialオブジェクトに対する<<演算子も実装している。

format()メソッドのパラメータの1番目dig_quoは出力桁数(符号部を含まず)を指定します。出力桁数に足りない場合(以下、不足桁)は空白または0で埋めます。2番目のパラメータoptは書式を指定します。

optパラメータ内容
DIVFMT::STD標準的な出力で、不足桁は空白で埋め、負の値に限り-を付加します。
DIVFMT::PAD_ZERO不足桁は0で埋めます。
DIVFMT::SIGN_PLUS正の値にも+符号を付加します。
DIVFMT::PAD_ZERO_SIGN_PLUS不足桁は0で埋め、正の値にも+符号を付加します。
DIVFMT::SIGN_SPACE正の値の場合は+符号の替わりに空白を付加します。
DIVFMT::PAD_ZERO_SIGN_SPACE不足桁は0で埋め、正の値の場合は+符号の替わりに空白を付加します。

//// div_result_i32オブジェクトから直接出力
Serial << div100(-1234) << crlf;
// 結果: -12.34

//// 3桁で出力します
Serial << div100(3456).format(3, DIVFMT::PAD_ZERO_SIGN_PLUE) << crlf;
// 結果: +034.56

//// c_str()を使ってconst char*を得る
char str1[128];
auto x = div100(-5678);
mwx_snprintf(str1, 16, "VAL=%s", x.format.c_str()); // const char*
Serial << str1;
// 結果: VAL=-56.78

背景

TWELITE BLUE/RED では除算はコストが高い演算であるため、目的を限定した除算アルゴリズムを追加した。

ライブラリ内では温度・湿度といった一部のセンサー値、100倍の値(25.12℃なら2512)を用いて表現しているため、100で割った商と余りを得るための簡素な手続きを定義した。

dev_result_i32::format()については、書式出力を行う際の煩雑さを避けるためである。

3.9 - Scale utils

最適化された値のスケーリング処理
8bit値(0..255など)の値とユーザが取り扱いやすい0..1000(千分率, ‰)値でスケール(拡縮)する。低い演算コストで実施するため、除算(x*1000/255)の替わりに乗算とビットシフトによって近似計算します。
static inline uint8_t scale_1000_to_127u8(uint16_t x)
static inline uint16_t scale_127u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_255u8(uint16_t x)
static inline uint16_t scale_255u8_to_1000(uint8_t x)
static inline uint8_t scale_1000_to_256u8(uint16_t x)
static inline uint16_t scale_256u16_to_1000(uint16_t x)

scale_1000_to_127u8()

0..1000 を 0..127 にスケールします。(16646*x+65000)>>17を用いて近似計算します。

scale_127u8_to_1000()

0..127 を 0..1000 にスケールします。(2064000UL*x+131072)>>18を用いて近似計算します。

scale_1000_to_255u8()

0..1000 を 0..255 にスケールします。(33423*x+65000)>>17を用いて近似計算します。

scale_255u8_to_1000()

0..255 を 0..1000 にスケールします。(1028000UL*uint32_t(x)+131072)>>18を用いて近似計算します。

scale_1000_to_256u8()

0..1000 を 0..256 にスケールします。(33554*x+66000) >> 17を用いて近似計算します。

注: x=999,1000は計算値が256になりますがuint8_tの範囲として255を返します。

scale_256u16_to_1000()

0..256 を 0..1000 にスケールします。(1028000UL*uint32_t(x)+131072)>>18を用いて近似計算します。

背景

ハードウェアに設定すべき値は0..255といった2進数が前提になることが多く、ユーザアプリケーションで取り扱う数は0..1000といった10進数基準のほうが扱いやすい。これらのスケール変換に除算を行わない式を定義した。

3.10 - pnew()

配置new の記述を簡素化する
配置new(placement new)の記述を簡素化します。
template <class T, class... Args>
T* pnew(T& obj, Args&&... args) {
    return (T*)new ((void*)&obj) T(std::forward<Args&&>(args)...);
}

例えば以下のように利用します。コンストラクタ引数を与えることもできます。

class my_class {
    int _a;
public:
    my_class(int a = -1) : _a(a) {}
};

my_class obj_1; // このコンストラクタは呼ばれない!
my_class obj_2; // このコンストラクタは呼ばれない!

void setup() {
    mwx::pnew(obj_1);    // my_class obj_1; に相当
	mwx::pnew(obj_2, 2); // my_class obj_2(2); に相当
    ...
}

背景

コンパイラの制約のためグローバルオブジェクトのコンストラクタが呼び出されないため、これを初期化する方法は配置newを使用する方法が挙げられます。しかしながら、配置new(placement new)の構文は煩雑に見えるため。

他にstd::unique_ptr(または eastl::unique_ptr)を用いる方法もある。

std::unique_ptr<my_class> obj_3;

void setup() {
    obj_3.reset(new my_class(3));
    		// TWELITE マイコンでは new は1回だけ確保し delete はできないため、
            // 事実上グローバルオブジェクトと同等です。
}