Передача указателей на функции в C ++ - PullRequest
5 голосов
/ 31 декабря 2008

Я хочу сделать этот простой кусок кода.

#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, используя против. Любая помощь будет очень ценится.

Ответы [ 5 ]

11 голосов
/ 31 декабря 2008

CreateThread требует 2 аргумента: указатель на функцию, выполняемую как поток, и аргумент DWORD, который будет передан потоку. ваша функция spawnThread () имеет только 1 аргумент (threadName); Вы думаете, , что имеет 2 аргумента из-за "i", но это действительно часть определения типа "threadName". (вы также можете опустить «i»; то есть вам не нужно называть аргументы как «threadName».)

в любом случае, учитывая, что вам НУЖНО 2 аргумента, переопределите spawnThread:

   void spawnThread(void (*threadEntryPoint)(int), int argument)
   {
      CreateThread(0,0,
                   (LPTHREAD_START_ROUTINE)threadEntryPoint,
                   (LPVOID)argument,
                   0,0);
   }

обратите внимание, что я не name аргумент int для threadEntryPoint; достаточно сказать компилятору, что функция должна иметь один аргумент int.

и назовите его:

   spawnThread(printSome, 155);

в любом случае, быстро и грязно, это будет делать то, что вы хотите.

НТН.

Рейли.

6 голосов
/ 31 декабря 2008

Лично я бы не стал передавать указатель на функцию, как вы пытаетесь сделать так же, как в 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);
    }

Вот пример кода из одного из наших классов, который делает это:

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());

}
5 голосов
/ 31 декабря 2008

Вы не можете передавать информацию о параметре в указателе функции; это должно быть передано отдельно. Именно поэтому функция CreateThread предоставляет параметр void *, который может указывать на все, что вы хотите.

Кроме того, вы должны использовать _beginthread вместо CreateThread для приложений 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;
}
4 голосов
/ 31 декабря 2008

Как уже упоминалось здесь, многие люди не могут передавать указатель на функцию и аргумент, с которым она должна вызываться, в одном параметре.

Ваша линия

    spawnThread(printSome(155));

«следует» (в мире DWIM) означает «вызвать printSome в отдельном потоке с аргументом 155». Однако C ++ не понимает этого. C ++ видит «Передать результат printSome, вызванного на 155, как параметр в spawnThread». Другими словами, последовательность шагов:

  • вызовите prinotSome с аргументом 155. Сохраните его во временной памяти.
  • call spawnThread с содержимым временной памяти в качестве аргумента.

Чтобы сделать то, что вы на самом деле имеете в виду, вы должны использовать C ++ и отделить аргумент от функции. Как это сделать, уже объяснено в других ответах. Короче говоря, это:

callOnOtherThreadWithArgument (функция, целое число);

1 голос
/ 31 декабря 2008

Вы можете прочитать, как вы это делаете здесь: http://www.newty.de/fpt/fpt.html

2.6 Как передать указатель на функцию в качестве аргумента?

Вы можете передать указатель на функцию как вызывающий аргумент функции. Тебе нужно это, например, если вы хотите передать указатель на функцию обратного вызова. следующий код показывает, как передать указатель на функцию, которая возвращает int и принимает float и два символа:

//------------------------------------------------------------------------------------
// 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...