Определение функции внутри вызова функции - PullRequest
1 голос
/ 28 января 2012

У меня была идея сэкономить время, включая создание временной функции для использования в качестве аргумента функции, которая в ней нуждается. Причина, по которой я придерживаюсь этого поведения, заключается в том, чтобы выполнять задачи в новом потоке простым способом (с использованием 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. Я хотел бы знать, насколько стандартен этот метод, и где я могу узнать, как использовать его для более продвинутых методов. Кроме того, я знаю, что использование его в функции, которая будет хранить его для последующего использования (например, функция цикла сообщений, в которую можно добавлять сообщения для обработки и соответствующую функцию для вызова), является плохой идеей, поскольку функция временная и не будет может быть вызван при необходимости. Действительно ли есть какое-то применение помимо вещей, таких как потоки, когда раздражает, что одна строка функционирует где-то еще, чтобы использовать ее?

Ответы [ 2 ]

2 голосов
/ 28 января 2012

Это называется "лямбда". Они очень полезны для многих целей помимо этого и находятся в Стандарте 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 голосов
/ 28 января 2012

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

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 :: string в STL. Поскольку он занимает динамическую память, которая может быть освобождена вашим начальным потоком, новый поток, работающий параллельно, должен иметь гарантию, что текст сообщения все еще будет доступен для него. Это можно сделать, скопировав (что будет работать с лямбда-захватом по значению - [=] вводчик) или разделив ссылку (через подсчет ссылок, скажем так). Давайте рассмотрим копирование:

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.
}

Поскольку память управляется CRT, CRT необходимо инициализировать в новом потоке, что не выполняется API CreateThread. Вместо этого вы должны использовать 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:)

спасибо)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...