MFC VC++6.0、XP
関数のポインタを返す、メンバ関数を作りたいのですが、コンパイルエラーがでてしま
います。どうすればいいのでしょうか?
double (*p) (double) CSParser::GetFunctionPointerDouble1 (const LPCSTR
&lpszFunctionName) //この行でエラーがでる
{
if (!strcmp( lpszFunctionName,sin)) {
return(sin) ;
}
return(cos) ;
} ;
エラー::error C2629: 'double (' は不正です
とりあえず typedef を使うと簡単。
下記の typedef が何を宣言しているのか理解できる?
typedef double ddfunc_type(double);
ddfunc_type* myclass::myfunc(const char* p) { ... }
理解できたら typedef を使わない記述方法を *自分で* 模索してみるといい。
(strcmp しまくるのは非常に遅いので実用に供する前に再考慮)
tetrapodさんの言うとおり「関数名」でスイッチするのは
やめたほうが良いかも。んで、以下にサンプル。
#include <math.h>
typedef double ( __cdecl *TriFunc)( double _X);
TriFunc TriFunc_Get( int Type)
{
switch( Type){
default:
case 0: return sin;
case 1: return cos;
case 2: return tan;
}
}
void foo()
{
double d = TriFunc_Get( 1)( double( 0)); // cos(0)
}
深く考えないで,意外ととこうでもよかったりして。。。
double CSParser::三角関数Function(int type,double value)
{
double dblResult =0.0;
switch( type ){
default:
case 0: dblResult =sin(value);
case 1: dblResult =cos(value);
case 2: dblResult =tan(value);
}
return dblResult;
};
おっと,関数ポインタの勉強にはなりませんね
あ,breakを忘れてますね。。。
tetrapod さん、仲澤@失業者さん、レスありがとうございます。
tetrapod さんの記述をそのまま引用すると、やりたいことは、できたのですが、
typedef を使わない形がよくわかりません。
仲澤@失業者さんの書いてくださった、__cdecl は、特に必要ないのかな?と考えてい
ます。(マニュアルを読んだのですが、コンパイラオプションがどうのこうので、よく
わかりません。)
hiroccoさんレスありがとうございます。
実際はCEditCtrlに入力された文字を計算するので、文字比較は、必要なのですが、
double CSParser::三角関数Function(int type,double value)
{
double dblResult =0.0;
switch( type ){
default:
case 0: dblResult =sin(value);
case 1: dblResult =cos(value);
case 2: dblResult =tan(value);
}
return dblResult;
};
でいいかな?という気がします。いや、このほうがいいかな?とも・・
実際にはmath.hのライブラリをほとんどサポートするつもりで、
pRetDArgumentD CSParser::GetFunctionPointerRD_AD (const LPCSTR
&lpszFunctionName) const
{
static struct SFunctionArgument1 {
char Name[FUNCTION_NAME_MAX];
double (*FunctionPointer) (double) ;
} FunctionArgument1[] = {
{sin,sin},
{cos,cos},
{tan,tan},
{asin,asin},
{acos,acos},
{atan,atan},
{sqrt,sqrt},
{log,log},
} ;
int i,i_max = sizeof(FunctionArgument1)/sizeof(SFunctionArgument1) ;
for (i = 0;i < i_max;i++) {
if(!strcmp(lpszFunctionName,FunctionArgument1[i].Name)) {
return(FunctionArgument1[i].FunctionPointer);
}
}
return((pRetDArgumentD)NULL) ;
}
とこんな感じにしているのですが・・・う~ん、考えどころですね。
でもね,sinはdoubleだけじゃないのよねぇ
あと,ここからは私の勝手な考えですけど,
やっぱり仲澤@失業者さんが言う通りインデックス?フラグ?で分けたほうがいいと思
いますよ
で,defineなりenumで定義しとくと後で絶対楽だから
そして,字句解析が必要ならその前段で解析させて,そのインデックス?フラグ?を調べ
て,渡すだけ
そうしておけば,字句解析を伴わない場面でもそのメソッドはまた有用になると思います
字句解析のモジュールと実際の計算するモジュールと別に設計したほうが綺麗かなぁっ
てね
ΣとかΠとか∫とか絡んでくると分けてないと私は頭おかしくなっちゃいますねwww
>math.hのライブラリをほとんどサポート...
引数の型も数も違うのとかありますけど。。。
hirocco さん、レスありがとうございます。
字句解析は、いれてありますが、関数名は、文字列のままです。
将来的に、ユーザ定義関数をいれようと思っているので、そうしたほうがいいかも?
と思ってそうしています。(といっても心はゆれていますが・・)
>でもね,sinはdoubleだけじゃないのよねぇ
long doubleのことをおっしゃってるのと思うのですが、ユーザから見て、sin()一つを
long double で使えると、いいかな?と思っています。
もし、文字列のままだと、case文の代わりにstrcmp()のような比較関数がずらずら並ぶ
ということになるのですね・・・。う~ん。どうしようなぁ・・・
> もし、文字列のままだと、case文の代わりにstrcmp()のような比較関数がずらずら並
ぶ
> ということになるのですね・・・。う~ん。どうしようなぁ・・・
だったら連想配列を使ってみるのは如何でしょうか?
std::map<std::string, ddfunc_type*> func_table;
最初に初期化が必要ですが、アクセスはある程度早くなるでしょう。
(実際には計測していないので効果のほどは...)
もっとも、文字列比較がどの程度性能に影響があるかは疑問です。
確かに
> (strcmp しまくるのは非常に遅いので実用に供する前に再考慮)
には同意しますが、全体のバランスが重要。一連の処理が0.1秒で終わるものなら
それほど気にすることはないと考えます。
だからこそ、「再考慮」と言われているのだと思いますが。
字句解析があるのならば、その時点で記号化しておくほうが効率はいいでしょうね。
(仲澤@失業者さんの意見と同様)
ただし、私なら、switch / case 文ではなく表引きにします。
あくまで私の考え方ですが、
文字列必須なら、連想配列(std::mapなど)を利用して検索した方がいい。
文字列必須じゃなく、分かりやすく区別したいだけならenumがお勧め。
enum FuncVal
{
FV_sin,
FV_cos,
FV_tan,
FV_Last // <- 最終要素
};
typedef double (*FunctionPointer)(double);
FunctionPointer Func[FV_Last];
Func[FV_sin] = sin;
Func[FV_cos] = cos;
Func[FV_tan] = tan;
こうすれば、添え字アクセスで検索の必要がない。
デバッグ時も添え字の値が 0, 1, 2 じゃなく、FV_sin, FV_cos, FV_tanなの
で分かりやすい。
>typedef を使わない形がよくわかりません。
>typedef double ddfunc_type(double);
>ddfunc_type* myclass::myfunc(const char* p) { ... }
↓
double (*myclass::myfunc(char const *))(double) { ... }
ちなみに、文字列比較時に英大小文字の違いは有になるのでしょうか?
まぁ、map使うにせよ、比較前に小文字に変換して、小文字同士で比較するだけですけど。
C/C++ のポインタ関連の記述方法は不必要に複雑/煩雑な上に、この例は
typedef を使って1段階簡単にしてやるととても読みやすくなる典型なわけで
あえてプレーンな書き方をせずに typedef したものにしておく・・・
というのは選択肢の1つとして「あり」だと思う。
字句的書き方講座
・ classname::funcname はこれで1つの名前なので分かち書きしない
・ふつーに 記述したときに [名前] が入るところが置き換わる
・引数のカッコと結合順を変えるカッコを区別する
double myfunc(float) /// 関数 myfunc あえて引数は float としておく
double (*myfuncptr)(float) /// 変数 myfuncptr は、関数へのポインタ
// 上記で (float) は引数のカッコ
// double *myfuncptr(float) とすると 引数のカッコは myfuncptr にくっつくので
// 先に *myfuncptr を評価させるためには別カッコが必要
double (*myfuncgetter(int))(float) /// 関数 myfuncgetter は関数ポインタを返す
// float の後ろに 変数名がいらないことに注目
double (*myclass::myfuncgetter(int funcno))(float) {
return functable[funcno];
}
ぢゃあここで課題
・関数ポインタのテーブル functable (bun 氏の Func[]) を記述して味噌
・POSIX の signal 関数は void (*signal(int, void (*func)(int)))(int); という
プロトタイプ宣言される。解読して味噌。 typedef を使って簡単にして味噌。
>仲澤@失業者さんの書いてくださった、__cdecl は、特に必要ないのかな?と考えてい
>ます。(マニュアルを読んだのですが、コンパイラオプションがどうのこうので、よく
>わかりません。)
「よくわからないときは無視してよい」という考え方は多くの場合、誤りです(vv;)。
typedef を使わない書き方を「練習」してみるのは良いことです。
tetrapodさんの秀逸な説明をよく読んでやってみてください。
ですが、すぐにいやになると思います。特にtemplate<>が絡むと最悪で、
そういったソースを見ると、書いた人の「おつむのでき」に驚嘆します(笑い)。
あったまいい人はありえないくらいあったまいいですもんね
私は基本的にできるだけ複雑にしない派ですねぇ
後で自分でも読めなくなっちゃうんで。。。
でも,練習というよりパズル感覚でいけば楽しいかもねぇ
私も趣味だったらハマル派です
でも,お仕事以外はパーソナルコンピュータなんて見たくもない