C ++での関数ポインターの受け渡し
-
03-07-2019 - |
質問
iこの簡単なコード作業を行いたい。
#include <iostream>
#include <windows.h>
void printSome (int i)
{
std::cout << i << std::endl;
}
void spawnThread (void (*threadName)(int i))
{
CreateThread
(
0, // default security attributes
0, // use default stack size
(LPTHREAD_START_ROUTINE)threadName, // thread function name
(LPVOID)i, // argument to thread function
0, // use default creation flags
0 // returns the thread identifier
);
}
int main ()
{
spawnThread(printSome(155));
}
私はWindowsを使用していますが、vs。ヘルプを使用すると非常に手間がかかります。
解決
個人的に、あなたがC ++のようにやろうとしているような関数ポインタを渡すことは考えません。 C ++でCをコーディングしています
代わりに、そのことをクラスでラップします。大きな利点は、クラスをオーバーライドして、必要なメンバーをいくつでも持つことができることです。毎回パラメーターを取得するために、すてきなキャストトリックを実行する必要はありません。
コードは少し長いので、最後までプッシュしました。ただし、次のようなことができます:
class print_some : public basic_thread {
private:
int i;
public:
print_some (int i) : i(i) {};
action_callback () {
std::cout << i << std::endl;
}
}
int main () {
print_some printer (155);
}
これを行うクラスの1つからの実行例のコードを次に示します。
class basic_thread :
{
public:
basic_thread();
protected:
unsigned long m_ThreadId;
virtual void action_callback () {};
// Internal routine used to bridge between OS callback format and
// action_callback. *Must* be static for the OS.
static unsigned long __stdcall self_calling_callback (void *parameter);
}
...および.cpp内:
unsigned long __stdcall basic_thread::self_calling_callback (void *parameter) {
if (parameter) {
basic_thread * thread = reinterpret_cast<basic_thread *>(parameter);
thread->action_callback();
}
return 0; // The value returned only matters if someone starts calling GetExitCodeThread
// to retrieve it.
}
basic_thread::basic_thread () {
// Start thread.
m_Handle = CreateThread(NULL,
0,
self_calling_callback,
(PVOID)this,
0,
&m_ThreadId );
if( !IsHandleValid() )
throw StartException("CreateThread() failed", GetLastError());
}
他のヒント
CreateThreadには2つの引数が必要です。スレッドとして実行する関数へのポインターと、スレッドに与えられるDWORD引数です。 spawnThread()関数には1つの引数(threadName)しかありません。 &quot; i&quot;のために2つの引数があると思いますが、実際には&quot; threadName&quot;の定義の一部です。タイプ。 (&quot; i&quot;を省くこともできます。つまり、&quot; threadName&quot;の引数に名前を付ける必要はありません。)
とにかく、2つの引数が必要な場合、spawnThreadを再定義します:
void spawnThread(void (*threadEntryPoint)(int), int argument)
{
CreateThread(0,0,
(LPTHREAD_START_ROUTINE)threadEntryPoint,
(LPVOID)argument,
0,0);
}
threadEntryPointへのint引数を name しなかったことに注意してください。関数には単一のint引数が必要であることをコンパイラに伝えるだけで十分です。
そしてそれを呼び出す:
spawnThread(printSome, 155);
とにかく、早くて汚い、これはあなたが望むことをします。
hth。
reilly。
関数ポインタでパラメータ情報を渡すことはできません。個別に渡す必要があります。そのため、CreateThread関数は、必要なものを指すことができるvoid *パラメーターを提供します。
さらに、 CreateThreadの代わりに_beginthreadを使用する C ++アプリケーション用。
最後に、プログラムはスレッドが実行される前に終了する可能性が高くなります。したがって、無限ループに入るか、API呼び出しを使用してスレッドの終了を待つ必要があります。
以下は、 WaitForSingleObject を使用してブロックする作業バージョンです。スレッドが完了します。
#include <iostream>
#include <process.h>
#include <windows.h>
void
printSome(int i)
{
std::cout << i << std::endl;
}
HANDLE
spawnThread(void (*threadName)(int), int i)
{
return (HANDLE) _beginthread((void (*)(void*)) threadName, 0, (LPVOID) i);
}
int
main(int argc, char *argv[])
{
HANDLE threadHandle;
threadHandle = spawnThread(printSome, 155);
WaitForSingleObject(threadHandle, INFINITE);
return 0;
}
これと同じ状況を処理する、はるかにC ++ /オブジェクト指向の方法があります:
#include <iostream>
#include <process.h>
#include <windows.h>
class Thread {
static void proxy(void *arg) { (*(reinterpret_cast<Thread *> (arg)))(); }
HANDLE thread_;
public:
virtual ~Thread() {}
virtual void operator()() = 0;
void start() { thread_ = (HANDLE) _beginthread(Thread::proxy, 0, this);}
void waitForExit() { WaitForSingleObject(thread_, INFINITE); }
};
class Printer : public Thread {
int i_;
public:
Printer(int i) : i_(i) {}
void operator()() { std::cout << i_ << std::endl; }
};
int
main(int argc, char *argv[])
{
Printer p(155);
p.start();
p.waitForExit();
return 0;
}
すでに多くの人がここで言及しているように、関数ポインターと、1つのパラメーターで呼び出す必要のある引数を渡すことはできません。
あなたの行
spawnThread(printSome(155));
&quot; should&quot ;; (DWIMの世界では)「引数155で別のスレッドでprintSomeを呼び出す」という意味です。ただし、C ++がそれを理解する方法ではありません。 C ++は、「155で起動されたprintSomeの結果をspawnThreadのパラメーターとして渡す」を参照します。つまり、ステップのシーケンスは次のとおりです。
- 引数として155を指定してprinotSomeを呼び出します。一時メモリに保存します。
- 一時メモリの内容を引数としてspawnThreadを呼び出します。
あなたが本当に意味することをするためには、C ++をユーモアし、引数を関数から分離する必要があります。その方法は、他の回答ですでに説明されています。要するに:
callOnOtherThreadWithArgument(function、integer);
その方法については、 http://www.newty.de/をご覧ください。 fpt / fpt.html
2.6引数として関数ポインタを渡す方法?
関数ポインタを渡すことができます 関数の呼び出し引数。あなたが必要 たとえば、これを渡したい場合 コールバック関数へのポインター。の 次のコードは、 を返す関数へのポインタ intおよびfloatと2つのcharを取ります:
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
// <pt2Func> is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
int result = (*pt2Func)(12, 'a', 'b'); // call using function pointer
cout << result << endl;
}
// execute example code - 'DoIt' is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
cout << endl << "Executing 'Pass_A_Function_Pointer'" << endl;
PassPtr(&DoIt);
}