みなさんこんにちわ、こんばんわ
SAIです。
さて、前回はC言語で関数ポインター呼び出しを勉強しました。
これを使いこなせたら便利ですよね?
じゃ、これをC++言語でもやってみましょうか!
改造のもとにするコードは、↓この日記のソースにしましょう。
A-4は7セグを1秒ごとに数字をカウントするプログラムですね。
1秒ごとの呼び出しを改造したいので、改造すべき処理を抜粋すると、
以下のコードが1秒ごとに呼び出していましたね。
--------------------------------------------------------
CSegControlClass lSegCtrlClass(C_7SegSetter);
/* 3つを同時に点灯 */
for(int i=0 ; i < 100;i++)
{
lSegCtrlClass.SetOutput(i); // 1秒ごとにカウントアップ //
delay(1000);
}
-------------------------------------------------------
これを関数ポインタに変更すればいいわけです。(あっさり)
C言語の方法を流用して記載するとこんな感じになりますね。
-------------------------------------------------------
/* 定数定義 */
// 関数配列 //
typedef void (*pFunc)(int aNum);
pFunc FunctionList[] =
{
&CSegControlClass::SetOutput /* 機能1 7Seg表示関数 */
};
-------------------------------------------------------
~中略~
/* 3つを同時に点灯 */
for(int i=0 ; i < 100;i++)
{
(void)(FunctionList[0])(i); // 1秒ごとにカウントアップ //
delay(1000);
}
-------------------------------------------------------
・・・なのですが、
じつはこれ、ビルドエラーになります。
cannot convert 'void (CSegControlClass::*)(int)' to 'pFunc {aka void (*)(int)}' in initialization
言っている意味が分かりにくいですよね。
パッと見は”型があっていません”といっているように見えますが、
実際のところは、全然違うところにあるんです。
関数ポインタ呼び出しですが、
文字通り、関数のポインタから呼び出すというものです。
今回C言語で呼び出ししようとしている関数ですが、
クラスのメンバー関数です。
以前少し触れましたが、
クラスってC言語でいうと、構造体のようなもので、型を定義している状態であり、
関数のポインタが存在していない状態なのです。
(厳密には、関数はROM領域に配置されるんですが、直接実行できるものではないんです。とある情報が足りない。)
思い出してください。
構造体のポインタを示すためには、該当の構造体の実体が存在する必要がありますよね?
ROMに配置されて、どこのデータがその変数なのかわからないと、
アドレスにしたくても無理です。
また、実体にするにしても、構造体は定義を変えれば沢山作成できますよね。
同様に、クラスもたくさん作れるのです。
CSegControlClassの実体を複数個作成した場合、
どのクラスのどの変数や関数が、呼び出したいデータ(アドレス)になるのかわかりませんよね?
というわけで、エラーになるわけです。
なんとなく理解できたでしょうか?
実は今回のような使い方の場合、
関数ポインタというのは、C++言語の思想から少し外れているのです。
ただ単に関数を呼び出す程度であれば、C++言語には素晴らしい使い方が用意されています。
ちょっと長くなるので、本日はここでいったん区切ります。
次はC++言語でのコールバックの作り方をC++言語学びます。





























雑談
C++言語でも、状態遷移を作る際には、関数ポインタは大活躍します。
したがって、C++言語の場合、関数ポインタがだめというわけではないのです。
実は、この内容。
2000年代前半のころ・・・SAIが駆け出し2年の新人の頃です。
携帯電話のOSが、μITronからSymbianOSに代わる際の話です。
お客様から、とある状態遷移の処理をC++言語で、
関数ポインタを使ってメンテナンス性の良い美しいもの(コード)を実装しろと命令されて、
当時非常に悩みました。
メンテナンス性を考慮したら、const配列で定義する必要があるのですが、
そうすると、ビルド時点でアドレス確定しておく必要があります。
が、上記の通り、クラス内にメンバー関数を配置すると、
アドレスが決定していないので、ビルドが通るわけないのです。
C++言語で関数ポインタを使う = アドレスが確定している必要がある。
つまり、クラス生成後でないといけない。
これをどうやったらクリアできるのか?
SAIは、1日中悩みました。
(当時、セキュリティエリアでインターネット禁止でしたし、
そもそも当時はインターネットにその手の情報があまりありませんでした。)
そんな中、若造が悩んだ挙句に出た結論が、
ビルド時点で配置するには、staticとして関数を定義するしかない。
が、問題としては、C++言語の思想とはかけ離れているのですよね。
なぜかというと、Static関数からは、メンバ変数にアクセスできません。
ということは、クラス内の全関数と全変数がStaticになってしまいます。
こうなってくると、クラスにする意味が全くないのです。
お客さんの要望である、美しいものという内容に対して、
C++言語の思想としてはかけ離れるのです。
ただ、当時、それもC++言語があまり普及していない時代です。
駆け出し2年の若造が、ベースクラスをあまり使いこなしているわけもなく、
SAIは一人悩んだ挙句、1週間かけて3000ステップ程度の処理を作り上げました。
(ちなみに、先輩方はC言語の経験はベテランでしたが、
C++言語に関してはSAIに聞くレベルなので、質問しても期待できずです。)
1週間後、中間報告でお客さんに見せた瞬間、
static関数まみれでダメじゃないかとおしかりを受けました。
その後、お客さんがベースクラスについての情報を教えてくださったおかげで、
1週間かけて作り直すことができました。
というか駆け出し2年目にやらす作業じゃないと、今になって思いました。
後から知ったのですが、お客さんはSAIは若く見えるけど、そこそこベテランさんだと
勘違いされていたそうです。
この出来事は、良き?思い出になりました。




























