Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Шаблон класса


Автор: zss 17.12.2005, 19:28
Есть немного странная задача.

Нужно создать класс (на основе шаблонов) который мог бы вызывать функции 2 типов.

1 тип - обычная функция
2 тип - метод класса

например
Код

class Caller {
    private:
        typedef unsigned (__stdcall *PFUNC_THREAD)(void *);

        PFUNC_THREAD func_;
        Caller (const Caller& copy);
        Caller& operator = (const Caller& copy);

    public:
        Caller(PFUNC_THREAD func, void *arg)  : func_(func);
        virtual ~Caller () {}
        virtual void Call(void) const {
            (func_)(arg_);
        }
};


и
Код

template <class T>
class Caller {
    private:
        typedef unsigned (T::*Func)(void *arg);
        T *obj_;
        Func func_;
        void *arg_;

        Caller (const Caller& copy);
        Caller& operator = (const Caller& copy);

    public:
        Caller(T *obj, Func func, void *arg) :
               obj_(obj), func_(func), arg_ (arg){}

        virtual ~Caller () {}
        virtual void Call(void) const {
            (obj_->*func_)(arg_);
        }
};


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

Автор: Mayk 17.12.2005, 19:50
Метод в лоб, пишу на коленке, выдумывать элегантное решение в лом

Код


struct Caller_Impl
{
  virtual void call()=0;
};


template  <class _Tp>
struct Caller_Method : public  Caller_Impl
{
      Caller_Method (_Tp* ptr, void(_Tp::*f)(void*), void* arg) : m_ptr(ptr), m_arg(arg), m_func(f){}
      void call(){
        (m_ptr->*m_func)(m_arg);
      }    
     _Tp* m_ptr;
     void* m_arg;
     void (_Tp::*m_func) (void*);
};

struct Caller_Function : public  Caller_Impl
{
      Caller_Function(void (*f)(), void* arg) :  m_func(f), m_arg(arg){}
      void call(){
        m_func(m_arg);
      }    
     void* m_arg;
     void (*m_func) (void*);
};

struct Caller
{
     template<class _T>
     Caller(T* cl, void(T::*func)(), void* arg ){
          m_caller = new Caller_Method<T>(cl,func,arg);
     }

     Caller(void (*func)(), void* arg){
          m_caller = new Caller_Function(func,arg);
     }
    void call(){
       m_caller->call();
    }
    CallerImpl* m_caller;
};

Думаю идея понятна smile

Автор: zss 17.12.2005, 20:04
Mayk, спасибо - хорошее решение


Цитата(Mayk @ 17.12.2005, 19:50)
выдумывать элегантное решение в лом


а что - можешь еще лучше предложить smile

Автор: Mayk 17.12.2005, 20:24
Могу предложить попробовать обойтись без использования доп классов. Правда это нечитабельнее

Что-то типа (часть кода за вызов ф-ции и очевидный деструктор опущу)
Код

class Caller
{
 template<class _Tp>
 Caller(_Tp* ptr, void (_Tp::*fn)(), void* arg ){    
        typedef void (_Tp::*Method)(void*);

    call_impl = &Caller::callMethod<_Tp> ; 
    m_data = new char[sizeof(_Tp*) +  sizeof(Method) +  sizeof(void*)];  //m_data = { _Tp*, func(), arg}
    char* p = m_data;
    memcpy(p, ptr, sizeof(ptr));  p+= sizeof(ptr);
    memcpy(p, fn, sizeof(fn));  p+= sizeof(fn);
    memcpy(p, arg, sizeof(arg)); 
 }

 void call(){
   call_impl();
 }

 template<class _Tp>
 void callMethod(){
       char* p = m_data;
       _Tp* ptr;  
       Method* fn;
       void* arg;
        typedef void (_Tp::*Method)(void*);

    memcpy(ptr,p, sizeof(ptr));  p+= sizeof(ptr);
    memcpy(fn,p, sizeof(fn));  p+= sizeof(fn);
    memcpy(arg,p, sizeof(arg)); 
     
          (ptr->*m)(p);
 }

 void (Caller::*call_impl)(void*);
 сhar* m_data;
};

Не сказал бы что это лучше :-)
Вывод - очевидные решения предпочтительнее неочевидных smile

Автор: zss 17.12.2005, 21:12
Mayk, что-то никак не соберу

Код

class CallerImpl {

    private :
        CallerImpl (const CallerImpl& copy);
        CallerImpl& operator = (const CallerImpl& copy);

    protected :
        void *arg_;
        CallerImpl () : arg_ (NULL) {}
        virtual ~CallerImpl () {}

    public :
        virtual void Call(void) const = 0;
};
/*----------------------------------------------------------------------------*/
class StaticCaller : private CallerImpl {

    private :
        typedef unsigned (__stdcall *Func)(void *);
        const Func func_;

        StaticCaller (const StaticCaller& copy);
        StaticCaller& operator = (const StaticCaller& copy);

        virtual void Call (void) const { func_ (arg_); }

    public :
        StaticCaller (Func func, void *arg) : func_(func), arg_ (arg) {}
        virtual ~StaticCaller () {}

};
/*----------------------------------------------------------------------------*/
template <class T>
class ClassCaller : private CallerImpl {

    private :
        typedef unsigned (T::*Func)(void *arg);
        const T *obj_;
        const Func func_;

        ClassCaller (const ClassCaller& copy);
        ClassCaller& operator = (const ClassCaller& copy);

        virtual void Call (void) const { (obj_->*func_) (arg); }

    public :
        ClassCaller (T *obj, Func func, void *arg) :
                     obj_(obj), func_(func) { arg_ = arg; }
        virtual ~ClassCaller () {}


};

/*----------------------------------------------------------------------------*/
class MyClass {
    private :
        CallerImpl caller_;

    public :
        template <class T>
        MyClass (T *obj, unsigned (T::*Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new ClassCaller (obj, Func, arg);
        }

        MyClass (unsigned (__stdcall *Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new StaticCaller (Func, arg);
        }
};


не хочет так smile

Автор: Mayk 17.12.2005, 21:54
Почитай сообщения об ошибках smile

Код

class CallerImpl {
    private :
        CallerImpl (const CallerImpl& copy);
        CallerImpl& operator = (const CallerImpl& copy);
    protected :
        void *arg_;
        CallerImpl () : arg_ (NULL) {}
        virtual ~CallerImpl () {}
    public :
        virtual void Call(void) const = 0;
};
/*----------------------------------------------------------------------------*/
class StaticCaller : public CallerImpl {
    private :
        typedef unsigned (*Func)(void *);
        const Func func_;
        StaticCaller (const StaticCaller& copy);
        StaticCaller& operator = (const StaticCaller& copy);
        virtual void Call (void) const { func_ (arg_); }
    void* arg_;
    public :
        StaticCaller (Func func, void *arg) : func_(func), arg_ (arg) {}
        virtual ~StaticCaller () {}
};
/*----------------------------------------------------------------------------*/
template <class T>
class ClassCaller : public CallerImpl {
    private :
        typedef unsigned (T::*Func)(void *arg);
        const T *obj_;
        const Func func_;
        ClassCaller (const ClassCaller& copy);
        ClassCaller& operator = (const ClassCaller& copy);
        virtual void Call (void) const { (obj_->*func_) (arg); }
    public :
        ClassCaller (T *obj, Func func, void *arg) :
                     obj_(obj), func_(func) { arg_ = arg; }
        virtual ~ClassCaller () {}
};
/*----------------------------------------------------------------------------*/
class MyClass {
    private :
        CallerImpl* caller_;
    public :
        template <class T>
        MyClass (T *obj, unsigned (T::*Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new ClassCaller<T> (obj, Func, arg);
        }
        MyClass (unsigned (*Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new StaticCaller (Func, arg);
        }
};

Автор: zss 17.12.2005, 22:11
Mayk, я так понимаю ты подправил

Код

ClassCaller[b]<T>[/b]


мой косяк - упустил smile

Код

class StaticCaller : public CallerImpl {
    private :
        void* arg_;  // Зачем еще раз ?




говорит, что не может конвертнуть StaticCaller* в Callerimpl* - странно ...

Автор: Mayk 17.12.2005, 22:13
Цитата(zss @ 18.12.2005, 02:11)
void* arg_;  // Зачем еще раз ?

g++ ругался, что arg_ не найден.
Добавлено @ 22:15
Стоп. Понял.

Цитата(zss @ 18.12.2005, 01:12)
        StaticCaller (Func func, void *arg) : func_(func), arg_ (arg) {}

Так нельзя - arg_ принадлежит базовому классу, который УЖЕ проинициализирован. => инициализировать его нельзя

Автор: zss 17.12.2005, 22:18
Цитата(Mayk @ 17.12.2005, 22:13)
Так нельзя - arg_ принадлежит базовому классу, который УЖЕ проинициализирован. => инициализировать его нельзя


значит придется заводить arg_ для каждого наследника ?

З.Ы. Попробовал - не прокатило

Автор: Mayk 17.12.2005, 22:21
Цитата(zss @ 18.12.2005, 02:18)

значит придется заводить arg_ для каждого наследника ?

Не обязательно
Цитата(zss @ 18.12.2005, 01:12)
        ClassCaller (T *obj, Func func, void *arg) :
                    obj_(obj), func_(func) { arg_ = arg; }

Вот это работает smile
Добавлено @ 22:24
Цитата(zss @ 18.12.2005, 02:18)

З.Ы. Попробовал - не прокатило


Цитата(zss @ 18.12.2005, 01:12)
virtual void Call (void) const { (obj_->*func_) (arg); }

компилятор даже знает почему

Автор: zss 17.12.2005, 22:33
это ошибки из-за большого количества спиртного smile

Код

class CallerImpl {

    private :
        CallerImpl (const CallerImpl& copy);
        CallerImpl& operator = (const CallerImpl& copy);

    protected :
        void *arg_;
        CallerImpl () : arg_ (NULL) {}
        virtual ~CallerImpl () {}
        virtual void Call(void) const = 0;

};
/*----------------------------------------------------------------------------*/
class StaticCaller : private CallerImpl {

    private :
        typedef unsigned (*Func)(void *);
        Func func_;

        StaticCaller (const StaticCaller& copy);
        StaticCaller& operator = (const StaticCaller& copy);

        virtual void Call (void) const { func_ (arg_); }

    public :
        StaticCaller (Func func, void *arg) : func_(func), arg_ (arg) {}
        virtual ~StaticCaller () {}

};
/*----------------------------------------------------------------------------*/
template <class T>
class ClassCaller : private CallerImpl {

    private :
        typedef unsigned (T::*Func)(void *arg);
        T *obj_;
        Func func_;

        ClassCaller (const ClassCaller& copy);
        ClassCaller& operator = (const ClassCaller& copy);

        virtual void Call (void) const { (obj_->*func_) (arg_); }

    public :
        ClassCaller (T *obj, Func func, void *arg) :
                     obj_(obj), func_(func), arg_ (arg) {}
        virtual ~ClassCaller () {}


};

/*----------------------------------------------------------------------------*/


говорит
Код

'constructor' is not an unambiguous base class of 'class'    

Автор: Mayk 17.12.2005, 22:42
Цитата(zss @ 18.12.2005, 02:33)
        ClassCaller (T *obj, Func func, void *arg) :
                    obj_(obj), func_(func), arg_ (arg) {}

Ну и зачем? arg_ нельзя инициализировать. Он уже инициализирован в базовом классе. => надо писать
arg_=arg, как и было раньше.

Цитата(zss @ 18.12.2005, 02:33)
'constructor' is not an unambiguous base class of 'class'   

где говорит?

Автор: zss 17.12.2005, 22:51
Цитата(Mayk @ 17.12.2005, 22:42)
где говорит?



Код

class MyClass {
    private :
        CallerImpl* caller_;

    public :
        template <class T>
        MyClass (T *obj, unsigned (T::*Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new ClassCaller<T> (obj, Func, arg);
        }
        MyClass (unsigned (*Func)(void *), void *arg) : caller_(NULL) {
            caller_ = new StaticCaller (Func, arg); // здесь
        }
};



Автор: Mayk 17.12.2005, 23:41
Замени private наследование на public

Автор: zss 18.12.2005, 17:49
Mayk, спасибо - вроде собралось smile

Еще вопрос - эти 2 конструктора придется всегда за собой таскать и для порожденных классов ? Если да - то можно ли что-нибудь придумать (чтоб не таскать - хотя есть большие подозрения, что нельзя smile)

Автор: Earnest 19.12.2005, 17:04
Если это не учебная задача, а практическая, лучше использовать готовое решение, например boost::function + boost::bind.
Все-таки это чисто техническая задача, и тратить время на ее решение (когда она уже решена так мощно, как в бусте), не целесообразно.
Стоит только посмотреть на бустовский код, чтобы понять трудозатраты на разработку подобных вещей...
Не обязательно использовать именно boost, есть еще Loki и другие подобные библиотеки.

Автор: zss 19.12.2005, 20:29
да вроде результат достигнут.
а по boosr у меня нет инфы

Автор: ParaPik 8.1.2009, 18:09
Добрый вечер.
У меня один пробел остался с использованием шаблонов. Даже в книгах Страуструпа не нашел ответа.
Я хочу спецализировать конструктор в классе. Я могу написать вот так:
Код

template <class T, class S>
class My_class
{
 //...
};
template <>
My_class<int, double>::My_class()
{
}

Но если пишу так:
Код

template <class S>
My_class<int, S>::My_class()
{
}

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

Автор: vinter 8.1.2009, 19:00
Здравствуйте, добро пожаловать на форум!

частичная специализацию функций запрещена(если мне память не изменяет).


Автор: mes 8.1.2009, 19:02
Цитата(vinter @  8.1.2009,  18:00 Найти цитируемый пост)
частичная специализацию функций запрещена(если мне память не изменяет).

используйте перегрузку, вместо частичной специализации

Автор: UnrealMan 8.1.2009, 21:07
Цитата(mes @  8.1.2009,  19:02 Найти цитируемый пост)
используйте перегрузку, вместо частичной специализации

Гениальный совет smile
Покажи же, как сие чудо будет выглядеть применительно к

Код
template <class S>
My_class<int, S>::My_class() /* здесь может быть список инициализации */
{
    /* здесь может быть тело конструктора */
}



Автор: ParaPik 8.1.2009, 22:39
Т.е. либо специализировать полностью функцию, либо специализировать весь класс. А случайно никто не знает, почему Страуструп решил так спроектировать использование шаблонов?(какова причина)

Автор: ParaPik 10.1.2009, 12:23
Как я понимаю таких данных ни у кого нет. А случайно в стандарте не будет ответа на мой вопрос?

Автор: vinter 10.1.2009, 13:05
Цитата(ParaPik @  10.1.2009,  13:23 Найти цитируемый пост)
А случайно в стандарте не будет ответа на мой вопрос?

проще всего посмотреть самому, чем ждать, что это сделают за тебя.

Цитата(ParaPik @  8.1.2009,  23:39 Найти цитируемый пост)
А случайно никто не знает, почему Страуструп решил так спроектировать использование шаблонов?

С++ сейчас не является полностью творением Страуструпа, над С++ работает целый комитет по стандартизации. Если я ничег не путаю, то отсутсвие часьтичной специализации функций это недоразумение которое будет исправлено в С++09

Автор: mes 10.1.2009, 13:36
Цитата(UnrealMan @  8.1.2009,  20:07 Найти цитируемый пост)
Покажи же, как сие чудо

Вероятно опять неправильно оценили высказывание:

Цитата(mes @  8.1.2009,  18:02 Найти цитируемый пост)
используйте перегрузку, вместо частичной специализации 

оно не говорит, что перегрузка заменит во всем специализацию,
а о том, что при разработке автоматизацию следует искать в перегрузке, а не частичной специализации.
И подход соответсвенно надо выбирать соответствующий. smile


Автор: UnrealMan 10.1.2009, 15:10
Цитата(mes @  10.1.2009,  13:36 Найти цитируемый пост)
при разработке автоматизацию следует искать в перегрузке, а не частичной специализации.
И подход соответсвенно надо выбирать соответствующий.

Это ты сам придумал или вычитал из какой-нибудь статьи под названием "Советы пьяного ёжика"?

Автор: mes 10.1.2009, 15:21
Цитата(UnrealMan @  10.1.2009,  14:10 Найти цитируемый пост)
Это ты сам придумал или вычитал

Нельзя ли точней указать на тот момент, который вы подвергаете сомнению - иначе боюсь мы будем обсуждать разное. 

Автор: UnrealMan 10.1.2009, 16:04
Цитата(mes @  10.1.2009,  15:21 Найти цитируемый пост)
Нельзя ли точней указать на тот момент, который вы подвергаете сомнению - иначе боюсь мы будем обсуждать разное.

Я могу подвергать сомнению только утверждения, смысл которых мне полностью ясен.
Ты так и не ответил на мой вопрос.

Автор: mes 10.1.2009, 19:45
Цитата(UnrealMan @  10.1.2009,  15:04 Найти цитируемый пост)
Ты так и не ответил на мой вопрос. 

Я не ответил, потому что не понял, о чем конкретно Вы спрашивуете smile
Исходя из вышесказанного, я лишь могу ответить : прямого цитирования чего либо там не было.

Добавлено через 1 минуту и 18 секунд
Цитата(UnrealMan @  10.1.2009,  15:04 Найти цитируемый пост)
смысл которых мне полностью ясен.

Ну а показать, где неясности в высказываниях возможно ?

Автор: UnrealMan 10.1.2009, 20:52
Цитата(mes @  10.1.2009,  19:45 Найти цитируемый пост)
Я не ответил, потому что не понял, о чем конкретно Вы спрашивуете 

Я спрашивал о том, что утверждалось в процитированном тексте. Или там ничего не утверждалось? smile

Цитата(mes @  10.1.2009,  19:45 Найти цитируемый пост)
Ну а показать, где неясности в высказываниях возможно ? 

Цитата(mes @  10.1.2009,  13:36 Найти цитируемый пост)
при разработке автоматизацию следует искать в перегрузке, а не частичной специализации.

Какую автоматизацию?

Вот так твоя писанина выглядит со стороны: "при разработке бла-бла-бла следует искать в перегрузке, а не частичной специализации.
И подход соответсвенно надо выбирать соответствующий." Я могу лишь читать текст, который ты пишешь, но не могу угадывать твои мысли. Бесплатный совет: если хочешь, чтоб тебя понимали, при выражении своих мыслей в тексте попробуй поставить себя на место читателя, который не осведомлён насчёт твоих заморочек, потому что о них может быть известно только тебе. 

Автор: ParaPik 10.1.2009, 22:18
Ну, во первых, хватит оффтопить. Как никак мы здесь шаблоны обсуждаем.
vinter, ты не мог сказать откуда у тебя сведения о С++09. Может ссылку кинешь.

Автор: vinter 10.1.2009, 22:23
http://www.open-std.org/JTC1/SC22/WG21/ оригинал
обсуждения:
http://habrahabr.ru/blogs/cpp/25330/
http://forum.sources.ru/index.php?showtopic=190375&st=0

Автор: mes 11.1.2009, 02:58
Цитата(UnrealMan @  10.1.2009,  19:52 Найти цитируемый пост)
Бесплатный совет: если хочешь, чтоб тебя понимали, при выражении своих мыслей в тексте попробуй поставить себя на место читателя, который не осведомлён насчёт твоих заморочек, потому что о них может быть известно только тебе. 

Можете мне не верить, но я всегда стараюсь (и учусь) подобрать выражение и исправиться сли поняли меня не корректно. smile 

Цитата(UnrealMan @  10.1.2009,  19:52 Найти цитируемый пост)
Какую автоматизацию?


Цитата("Википедиа")

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

на этом основание я отнес шаблоны, специализацию, и перегрузку к средствам автоматизации.
И имхо не только я, потму что употребления этого слова (автоматизация) в таком контексте встречалась не раз как в литературе, так и на форумах.

Цитата(UnrealMan @  10.1.2009,  19:52 Найти цитируемый пост)
Я спрашивал о том, что утверждалось в процитированном тексте. 

Цитата с автоматизацией была пояснящая предыдущую цитату.. но к сожаление ей не удалось выполнить задачу smile

Основная цитата эта :
Цитата(mes @  8.1.2009,  18:02 Найти цитируемый пост)
используйте перегрузку, вместо частичной специализации 

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

Добавлено через 3 минуты и 26 секунд
Цитата(ParaPik @  10.1.2009,  21:18 Найти цитируемый пост)
Ну, во первых, хватит оффтопить. Как никак мы здесь шаблоны обсуждаем. 

Так вроде как раз по теме. Идет обсуждение на правильность одного из ответов. smile

Автор: UnrealMan 11.1.2009, 04:11
Цитата(mes @  11.1.2009,  02:58 Найти цитируемый пост)
на этом основание я отнес шаблоны, специализацию, и перегрузку к средствам автоматизации.

Замечательно. Только вот смысл той фразы от этого как-то не проясняется.

Цитата(mes @  11.1.2009,  02:58 Найти цитируемый пост)
Попробую перефразировать еще раз :
Когда нужно шаблонной функции задать определенное поведение , для определенных типов параметров, то за неимением частичной специализации, можно попытаться найти решение с помощью перегрузки.

Ну, это совсем не то же самое, что

Цитата(mes @  8.1.2009,  19:02 Найти цитируемый пост)
используйте перегрузку, вместо частичной специализации 

"Используйте" и "рассмотрите вариант с использованием" - не одно и то же.

Вместо частичной специализации функции можно рассмотреть вариант с использованием частичной специализации вспомогательного класса, функция-член которого будет содержать всю необходимую реализацию:

Код
template <class T>
struct f_impl
{
    void operator()() const
    {
        ....
    }
};

template <class T>
struct f_impl<T *>
{
    void operator()() const
    {
        ....
    }
};

template <class T>
void f()
{
    f_impl<T>()();
}

Тоже вполне известный подход наравне с перегрузкой.

Автор: mes 11.1.2009, 04:41
Цитата(UnrealMan @  11.1.2009,  03:11 Найти цитируемый пост)

"Используйте" и "рассмотрите вариант с использованием" - не одно и то же.

Постараюсь более точно подбирать глаголы smile
А все таки хорошо, что на фоурме есть такой придирчивый к словам человек  smile 

Автор: ParaPik 11.1.2009, 09:34
Спасибо, vinter. Наверное, все таки обсуждение по поводу корректного написания сообщений в тему не оффтоп.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)