Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать. Вот такой код вполне валиден, однако на VC6 не компилится (точнее, не инстанцируется):
template <class T, int (T::*f)()>
struct CProxy
{
static int ThreadProcStub(T *p)
{
return (p->*f)();
}
};
class A
{
public:
int onA()
{
return 1;
};
int onB()
{
return 2;
};
};
int (*f)(A *) = &CProxy<A, &A::onA>::ThreadProcStub; // invalid template argument
На "правильных" компиляторах, конечно, все ОК. Есть ли какой обходной путь добиться такого (т.е. фактически генерации нужной статической функции на основе типа класса и адреса его функции-члена) на VC6?
Спасибо.
ПК>Т.е. сделать искуственный вложенный шаблон класса с одним параметром — ПК>указателем на член.
Верно... И ведь уже раньше так делал, да и пост этот тоже видел. День,наверное, не задался
Только вот до сих пор для меня загадка, в чем VC видит отличие между этими вариантами. Шаманство, в общем
Павел, большое спасибо!
Andrew S wrote:
> Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать.
Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind.
ME>>Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind.
AD>Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL
AD>Хотя конкретно для данной задачи boost::threads подходят идеально.
Потоки в стиле VCL — это ужасное решение. Да прибудет с ними гиена огненная, пусть сгинут во мраке ада. Аминь.
А по поводу boost::threads скажу лишь одно — не плодите сущности без необходимости. Но за совет спасибо, будет время — ознакомлюсь.
On Thu, 22 Jan 2004 19:37:35 GMT, ArtDenis <15178@news.rsdn.ru> wrote:
> Здравствуйте, MaximE, Вы писали: > > ME>Радикальное решение — boost::threads. В качестве бонуса получаешь все прелести boost::bind. > > Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL
Непонятно "ассоциации данных с потоками". Объект потока не обязательно сохранять, т.к. разрушение этого объекта не завершает поток.
Здравствуйте, MaximE, Вы писали:
>> Лично мне концепция boost::threads кажется неудобной (по стилю ассоциации данных с потоками). Может быть потому, что я привык к потокам в стиле VCL ME>Непонятно "ассоциации данных с потоками".
Извиняюсь, слишком кратко написал. Представь себе, что ты пишешь многопоточный сервер. На одно подключение клиента — один поток. Для каждого потока — свой набор данных. В VCL это всё делалось элементарно: ты наследовался от класса потока, добавлял свои данные, а затем с ними работал. А как это сделано в boost::threads? Через thread_specific_ptr. Вот пример из буста:
AD>Извиняюсь, слишком кратко написал. Представь себе, что ты пишешь многопоточный сервер. На одно подключение клиента — один поток. Для каждого потока — свой набор данных. В VCL это всё делалось элементарно: ты наследовался от класса потока, добавлял свои данные, а затем с ними работал. А как это сделано в boost::threads? Через thread_specific_ptr. Вот пример из буста:
Здравствуйте, Andrew S, Вы писали:
AS>Всем доброго времени суток.
AS>Надоело постоянно писать статические функции-прокси для вызова функций-членов класса в отдельном потоке, захотелось это дело как то автоматизировать.
Почему бы не воспользоваться boost?
class thread_launcher
{
public:
typedef boost::function0<void> func_type;
private:
static int thunk(void* pv)
{
*pbf = (func_type*)pv;
(*pbf)();
delete pbf;
}
public:
static unsigned long launch(const func_type& fn)
{
return beginthread(thunk, 0, new func_type(fn));
}
template<class C>
static unsigned long launch(C* pobj, void(&C::method)())
{
return launch(func_type(pobj, method));
}
};
Перекуём баги на фичи!
Re[2]: member function as template argument (VC6) workaround
Да MaximE конечно красиво записал, но это не addresed to the problem.
Связь данных и потока так и остаётся за кадром.
Когда можно разрушить объект `С`? Из кода не ясно.
Данная схема позволяет запустить несколько параллельных потоков метода одного и того же объекта, что видимо далеко не всегда желаемо if at all.
Как я уже написал в предыдущем посте (правда забыл в инициализации привязаться через bind :-) различные схемы решения таких проблем вполне можно решить используя и бустовские классы.
Вопрос — стоило ли добавлять в boost такие обвязки?
Re[4]: member function as template argument (VC6) workaround
Здравствуйте, Xor., Вы писали:
X>Да MaximE конечно красиво записал, но это не addresed to the problem. X>Связь данных и потока так и остаётся за кадром. X>Когда можно разрушить объект `С`? Из кода не ясно.
Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока.
Интересный код, я так и не понял, что он делает
X>PS. вообще-то я очень поверхностно знаю boost, поэтому возможно, что есть варианты и получше.
Конечно же есть. В моём верхнем сообщении я как раз привёл пример из буста с таким решением. Просто лично мне оно не нравится.
AD>Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока.
Именно так. Более того, совершенно нежелательно ассоциировать экземпляр класса с потоком, т.к. потоков у одного экземпляра может быть и несколько, и они выполняют совершенно разные задачи. Судя по всему, реализовать это при помощи boost::threads очень даже можно, но так уж получается, что я предпочитаю не использовать высокоуровневые обертки там, где без этого можно обойтись. И бью других по рукам за излишнее усложнение кода там, где можно обойтись более простыми и архитектурно чистыми решениями.
Xor. wrote:
> Да MaximE конечно красиво записал, но это не addresed to the problem.
Насколько я понимаю, проблема была вызвать ф-цию член в отдельном потоке. boost::treads решает эту проблему. Или я что-то упускаю?
> Связь данных и потока так и остаётся за кадром.
Так и не понял смысл фразы "Связь данных и потока". Что это значит, что хорошо, что плохо?
> Когда можно разрушить объект `С`? Из кода не ясно.
Из кода ясно, что экземпляр класса C c — глобальный объект. Также ясно, что main() не вернет управления, пока не завершиться поток, направленный в функцию-член c.
> Данная схема позволяет запустить несколько параллельных потоков метода одного и того же объекта, что видимо далеко не всегда желаемо if at all.
Это преимущество данной схемы — не накладывает ограничений.
В ACE, например, чтобы направить поток в ф-цию-член, надо отнаследоваться от класса потока, и реализовать виртуальную ф-цию-член Run() (или что-то наподобие). Я если я хочу два потока в разные ф-ции-члены? Придется приложить усилия, чтобы сделать это при помощи такой схемы. (не говоря о том, что добавленние vtable в класс может быть не очень желательным)
> Как я уже написал в предыдущем посте (правда забыл в инициализации привязаться через bind различные схемы решения таких проблем вполне можно решить используя и бустовские классы. > > Вопрос — стоило ли добавлять в boost такие обвязки?
Какие именно "обвязки"? Если имеешь в виду boost::threads, то это не просто обвязки — эту мультиплатформенные обвязки (по крайней мере win32, POSIX, MAC), которые часто позволяют не опускаться до уровня API платформы.
Andrew S wrote:
> AD>Судя по всему, AndrewS сам контролирует жизнь объекта класса. В данном случае использование boost::threads позволяет отказаться от использования статических функций в классе для запуска потока. > > Именно так. Более того, совершенно нежелательно ассоциировать экземпляр класса с потоком, т.к. потоков у одного экземпляра может быть и несколько, и они выполняют совершенно разные задачи.
Не вижу связи, между частями сложного предложения. Понятия класс/экземпляр и поток совершенно ортогональны. Класс, данном контексте, — способ организации исходного кода. Поток — сущность, которая исполняет код.
Экземпляр класса может создать хоть M, или даже N потоков и направить их куда ему нужно.
> Судя по всему, реализовать это при помощи boost::threads очень даже можно, но так уж получается, что я предпочитаю не использовать высокоуровневые обертки там, где без этого можно обойтись. И бью других по рукам за излишнее усложнение кода там, где можно обойтись более простыми и архитектурно чистыми решениями.
Это вы, батенька, очень напрасно. boost::threads элегантно и эффективно решает рутинную задачу создания потоков. boost::threads документирован и его назначение недвусмысленно. ИМХО, намного проще читать и понимать код, который оперирует стандартными высокоуровневыми сущностями, чем код, который реализует те же задачи посредством низкоуровневых API вызовов.
Кроме того, использование сущностей высокого уровня позволяет инженеру сосредоточиться на problem domain, а не на том, например, в каком параметре передать указатель на ф-цию в вызове CreateThread() и не забыть проверить и правильно интерпретировать возвращаемое значение.