2012-01-28 24 views
1

У меня возникла идея сэкономить время, связанное с созданием временной функции для использования в качестве аргумента функции, которая в ней нуждается. Причина, по которой я следую этому поведению, - это сделать что-то в новом потоке простым способом (используя Win32 API) без необходимости определять все виды функций, которые я буду использовать.Определение функции внутри функции вызов

Вот пример:

void msg (const string & message) { 
    MessageBox (0, message.c_str(), "Message", 0); 
} 

Это даст окно сообщения, но ваша программа останавливается, пока она не закроется. Решение состоит в том, чтобы создать поток для окна сообщений, который запускается одновременно с основным потоком.

void msg (const string & message) { 
    CreateThread (0, 0, 
    (LPTHREAD_START_ROUTINE)({MessageBox (0, message.c_str(), "Message", 0);}), 
    0, 0, 0); 
} 

В этом случае LPTHREAD_START_ROUTINE определяется как
typedef DWORD (*LPTHREAD_START_ROUTINE)(LPVOID param);

Поскольку у меня есть несколько функций, желающих другой поток для целей, как, что, помещая функцию в вызове CreateThread, кажется, работает хорошо.

Но скажу, что я хотел использовать это LPVOID param. Я хотел бы знать, насколько стандарт этот метод, и где я могу узнать, как использовать его для более сложных методов. Кроме того, я знаю, что я использую его в функции, которая будет хранить его для последующего использования (например, функция цикла сообщений, где вы можете добавлять сообщения для обработки и соответствующую функцию для вызова) - это плохая идея, поскольку функция является временной и не будет который может быть вызван при необходимости. Существует ли какая-либо польза от таких вещей, как потоки, где раздражает, чтобы одна линия функционировала в другом месте, чтобы ее использовать?

+0

Вопрос не совсем понятно (а также, что синтаксис не является стандартным) –

+0

я просто была идея сегодня : схема сохранения времени и пространства в файле. Я в основном интересовался, насколько безопасно это использовать, и если его можно использовать для лучших вещей, чем этот пример. – chris

ответ

2

Это называется «лямбда». Они очень полезны для многих целей, кроме этого, и находятся в стандарте C++ 11. Вы можете найти их в последних GCC и MSVC. Тем не менее, текущая реализация MSVC не позволяет преобразовать указатель на функцию, так как стандарт не указал такое преобразование в это время. VC11 реализует это преобразование. Этот код также стандартный C++ 11:

void msg (const string & message) { 
    CreateThread (0, 0, 
    [](LPVOID* param) { MessageBox (0, message.c_str(), "Message", 0); }, 
    0, 0, 0); 
} 
+0

Это не 'LPVOID *' (= 'void **'), просто 'LPVOID' (=' void * ') (глупые Windows' typedef's) –

+0

Спасибо за этот термин. Это должно помочь облегчить болезненные страницы несвязанных тем при поиске. Одно: LPVOID определяется как 'void *', так что там есть дополнительный *. Угадайте, что Сет избил меня до этого ... – chris

0

Был еще один способ в разы лямбда не стандартная - вы могли бы определить функцию локального класса и определить внутреннюю функцию внутри этого класса. Что-то вроде:

void msg(const string &message) 
{ 
    struct message_box 
    { 
     static DWORD WINAPI display(LPVOID param) 
     { 
      /// do the action 
      return 0; 
     } 
    }; 

    ::CreateThread(0, 0, 
     &message_box::display, 
     0, 0, 0); 
} 

Теперь рассмотрим ваш вопрос. Вы хотели передать текст сообщения в STD std :: string. Поскольку он занимает динамическую память, которая может быть освобождена исходным потоком, новый поток, работающий параллельно, должен иметь гарантию, что текст сообщения по-прежнему будет доступен для него. Это можно сделать путем копирования (которое будет работать с привязкой по значению лямбда - [=] интродуктором) или обмена ссылкой (через подсчет ссылок, скажем). Давайте рассмотрим копирование:

void msg(const string &message) 
{ 
    struct message_box 
    { 
     static DWORD WINAPI display(void *param) 
     { 
      MessageBox(0, ((string *)param)->c_str(), "Message", 0); 
      delete (string *)param; 
      return 0; 
     } 
    }; 

    string *clone = new string(message); 

    ::CreateThread(0, 0, 
     &message_box::display, 
     clone, 0, 0); 
} 

Пожалуйста, обратите внимание, что копия выделяется в начальной нити и уничтожены в новом. Для этого требуется поддержка многопоточности от вашего CRT.

В случае, если новый поток не запускается, вы потеряете сиротскую память. Позволяет исправить:

void msg(const string &message) 
{ 
    struct message_box 
    { 
     static DWORD WINAPI display(void *param) 
     { 
      auto_ptr<string> message((string *)param); 
      MessageBox(0, message->c_str(), "Message", 0); 
      return 0; 
     } 
    }; 

    auto_ptr<string> clone(new string(message)); 

    if (::CreateThread(0, 0, &message_box::display, clone.get(), 0, 0)) 
     clone.release(); // release only if the new thread starts successfully. 
} 

Поскольку память управляется ЭЛТ, ЭЛТ должны быть инициализированы в новом потоке, который не делается с помощью CreateThread API.Вы должны использовать CRT BeginThread/beginthreadex вместо:

void msg(const string &message) 
{ 
    struct message_box 
    { 
     static unsigned int WINAPI display(void *param) 
     { 
      auto_ptr<string> message((string *)param); 
      MessageBox(0, message->c_str(), "Message", 0); 
      return 0; 
     } 
    }; 

    auto_ptr<string> clone(new string(message)); 

    if (_beginthreadex(0, 0, &message_box::display, clone.get(), 0, 0)) 
     clone.release(); // release only if the new thread starts successfully. 
} 

Это решение оставляет в стороне вопрос о резьбе сама будучи просочилась в качестве ресурса. Но, я полагаю, вы можете найти еще сообщения для этого на stackoverflow.com :)

спасибо)

+1

Проблема с CreateThread и C runtime была отправлена ​​в историю давным-давно. –

Смежные вопросы