Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 02.10.09 04:05
Оценка:
Доброго времени суток, Уважаемые.

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

#include <string>
#include <iostream>

using namespace std;

class A
{
public:
    static void create(string& str)
    {
        str += "A";
    }
};

class B : public A
{
// нет перегрузки, используем класса A
};

class C : public B
{
public:
    static void create(string& str)
    {
        B::create(str);
        str += "C";
    }
};

class D : public C
{
public:
    static void create(string& str)
    {
        C::create(str);
        str += "D";
    }
};

class E : public B
{
public:
    static void create(string& str)
    {
        C::create(str); // вот закралась ошибка, нужно B::create(str)
        str += "E";
    }
};

int main(int argc, char *argv[], char *env[])
{
    string str;
    
    A::create(str);
    cout << str << endl;
    str.clear();

    B::create(str);
    cout << str << endl;
    str.clear();

    C::create(str);
    cout << str << endl;
    str.clear();
    
    D::create(str);
    cout << str << endl;
    str.clear();

    E::create(str);
    cout << str << endl;
    str.clear();

    return 0;
};

результат выполнения(из-за copy-paste в E:create, ошибочно вызвали метод другого класса)
A
A
AC
ACD
ACE

требуемый результат выполнения
A
A
AC
ACD
AE

хотелось бы избавиться от необходимости явного указания вызова функции базового класса, чтобы это делал компилятор
как-то так
#include <string>
#include <iostream>

using namespace std;

class A
{
public:
    static void create(string& str)
    {
        str += "A";
    }
};

class B : public A
{
// нет перегрузки, используем класса A
};

class C : public B
{
public:
    static void create(string& str)
    {
        str += "C";
    }
};

class D : public C
{
public:
    static void create(string& str)
    {
        str += "D";
    }
};

class E : public B
{
public:
    static void create(string& str)
    {
        str += "E";
    }
};

int main(int argc, char *argv[], char *env[])
{
    string str;
    
    A::create(str);
    cout << str << endl;
    str.clear();

    B::create(str);
    cout << str << endl;
    str.clear();

    C::create(str);
    cout << str << endl;
    str.clear();
    
    D::create(str);
    cout << str << endl;
    str.clear();

    E::create(str);
    cout << str << endl;
    str.clear();

    return 0;
};


результат выполнения
A
A
AC
ACD
AE
перегруженные функции
Re: Цепочка вызовов перегруженных статических функций
От: rg45 СССР  
Дата: 02.10.09 05:17
Оценка: +1
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP> Доброго времени суток, Уважаемые.


AP>Как автоматизировать вызов статической перегруженной функции базововго класса?

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

AP>[ccode]

AP>#include <string>
AP>#include <iostream>

AP>using namespace std;


AP>class A

AP>{
AP>public:
AP> static void create(string& str)
AP> {
AP> str += "A";
AP> }
AP>};

AP>class B : public A

AP>{
AP>// нет перегрузки, используем класса A
AP>};

AP>class C : public B

AP>{
AP>public:
AP> static void create(string& str)
AP> {
AP> B::create(str);
AP> str += "C";
AP> }
AP>};

AP>class D : public C

AP>{
AP>public:
AP> static void create(string& str)
AP> {
AP> C::create(str);
AP> str += "D";
AP> }
AP>};

AP>class E : public B

AP>{
AP>public:
AP> static void create(string& str)
AP> {
AP> C::create(str); // вот закралась ошибка, нужно B::create(str)
AP> str += "E";
AP> }
AP>};

Маленькая поправочка: в данном случае имеет место не перегрузка функций, а сокрытие — C::create скрывает A::create, D::create скрывает C::create и т.д.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Цепочка вызовов перегруженных статических функций
От: Bell Россия  
Дата: 02.10.09 05:25
Оценка: 10 (3)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>хотелось бы избавиться от необходимости явного указания вызова функции базового класса, чтобы это делал компилятор

AP>как-то так
...
ИМХО так делать точно не стоит — наличие скрытого функционала никак не способствует улучшению читабельности кода.

Можно попробовать что-то типа вот этого:
template <class T>
class parent_t : public T
{
protected:
typedef T Base;
};

class A
{
public:
    static void create(string& str)
    {
        str += "A";
    }
};

class B : public parent_t<A>
{
// нет перегрузки, используем класса A
};

class C : public parent_t<B>
{
public:
    static void create(string& str)
    {
        Base::create(str);
        str += "C";
    }
};


ИМХО это несколько снижает вероятность ошибок.
Можно вместо шаьлона parent_t использовать платформенно-зависимые фичи, например __super в MSVC

class C : public parent_t<B>
{
public:
    static void create(string& str)
    {
        __super::create(str);
        str += "C";
    }
};
Любите книгу — источник знаний (с) М.Горький
Re[2]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 02.10.09 05:31
Оценка:
Здравствуйте, Bell, Вы писали:

B>class C : public parent_t<B>
B>{
B>public:
B>    static void create(string& str)
B>    {
B>        Base::create(str);
B>        str += "C";
B>    }
B>};
B>


Интересно, спасибо.
Можно как-то совсем избавиться от необходимости явного вызова перекрытой функции?
Re[3]: Цепочка вызовов перегруженных статических функций
От: Bell Россия  
Дата: 02.10.09 05:38
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Интересно, спасибо.

AP>Можно как-то совсем избавиться от необходимости явного вызова перекрытой функции?

Ну вот первое, что приходит в голову:

template <class D, class B>
class parent_t : public B
{
public:
   static void create(string& str)
   {
      B::create_impl(str);
      D::create_impl(str);
   }
};

class A
{
public:
    static void create(string& str) { create_impl(str); }
    static void create_impl(string& str)
    {
        str += "A";
    }
};

class B : public A
{
// нет перегрузки, используем класса A
};

class C : public parent_t<C, B>
{
public:
    static void create_impl(string& str)
    {
        str += "C";
    }
};


Но мне это решение не нравится.
Любите книгу — источник знаний (с) М.Горький
Re[2]: Цепочка вызовов перегруженных статических функций
От: Bell Россия  
Дата: 02.10.09 07:12
Оценка:
Здравствуйте, Bell, Вы писали:

B>Можно вместо шаьлона parent_t использовать платформенно-зависимые фичи, например __super в MSVC


B>
B>class C : public parent_t<B>
B>{
B>public:
B>    static void create(string& str)
B>    {
B>        __super::create(str);
B>        str += "C";
B>    }
B>};
B>


Не до конца исправил пример:
class C : public B
{
public:
    static void create(string& str)
    {
        __super::create(str);
        str += "C";
    }
};

Любите книгу — источник знаний (с) М.Горький
Re: Цепочка вызовов перегруженных статических функций
От: remark Россия http://www.1024cores.net/
Дата: 02.10.09 09:26
Оценка: 2 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Как автоматизировать вызов статической перегруженной функции базововго класса?

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

Есть. Специально для таких целей я и разрабатывал CRTP 3:
http://rsdn.ru/forum/cpp/2677311.aspx
Автор: remark
Дата: 02.10.07




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: Цепочка вызовов перегруженных статических функций
От: Кодт Россия  
Дата: 02.10.09 10:10
Оценка: 3 (1)
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Можно как-то совсем избавиться от необходимости явного вызова перекрытой функции?


Зависит от того, что из себя представляют классы.
Если они служат лишь для конструирования строки, то можно заменить на mpl-списки, например.
Если же это "просто классы", в которые ты хочешь добавить фичу...

Ну например, такое. Поскольку ты хочешь, чтобы реализация (create_impl) не парилась об организации забега по иерархии — вынеси этот забег наружу.
// официальный механизм наследования
struct root
{
    static void create_impl(string&) {}
};
template<class S> struct derive : S { typedef S superclass; };

// забег по классу
template<class T> struct runner
{
    static void create(string& s)
    {
        typedef typename T::superclass S;
        runner<S>::create(s);
        if( &T::create_impl != &S::create_impl ) T::create_impl(s); // можно вынести во время компиляции
    }
};
template<> struct runner<root>
{
    static void create(string&) {}
};


class A : public root { ..... };
class B : public derive<A> { ..... };
class C : public derive<B> { ..... };

int main()
{
    string s;
    runner<C>::create(s); // вместо C::create(s);
}


Либо по-простецки, на макросах — просто избавь себя от копипаста
#define DECLARE_CREATE_IMPL(s) \
    static void create(string& s) { __super::create(s); create_impl(s); } /* здесь мы определяем порядок обхода */ \
    static void create_impl(string& s) /* а здесь сразу же объявляем перегрузку - чтобы уже не отвертеться */ \
    //endmacro

#define DEFINE_CREATE_IMPL(T,s) void T::create_impl(string& s) /* для реализации вне объявления класса */

struct root { static void create(string&) {} };

struct A : root
{
    DECLARE_CREATE_IMPL(boo) // можем давать любые имена параметру
    // далее следует инлайновое определение create_impl(string& boo)
    {
        boo += "A";
    }
};

struct B : A
{
    DECLARE_CREATE_IMPL(x);
};
inline DEFINE_CREATE_IMPL(B,x) { x += "B"; }

struct C : B
{};

struct D : C
{
    DECLARE_CREATE_IMPL(s);
};
// где-то в .cpp
DEFINE_CREATE_IMPL(D,q) { q += "D"; }
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[2]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 05.10.09 03:13
Оценка:
Здравствуйте, remark, Вы писали:

R>Есть. Специально для таких целей я и разрабатывал CRTP 3:

R>http://rsdn.ru/forum/cpp/2677311.aspx
Автор: remark
Дата: 02.10.07


Спасибо, когда-то даже закладки поставил себе на CRTP 1,2,3 чтобы поразбираться с ними Видать, время пришло, поразбираюсь
Re[4]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 05.10.09 03:24
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Зависит от того, что из себя представляют классы.

Private переменные-параметры.
Т.е. представляют из себя иерархию свойств.
class BaseData {
int modAddr; //адрес устройства
};

class Mod1Data : public BaseData {
time_t lastEvent; // время последнего события
};

class Mod2Data : public BaseData {
time_t onLen; // продолжительность включения
};

эти данные намерен хранить в разделяемой памяти, поэтому понадобился механизм вложенного "конструктора"
Автор: Alexander Pazdnikov
Дата: 01.10.09
Re[4]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 05.10.09 03:53
Оценка:
Здравствуйте, Кодт, Вы писали:


К>Ну например, такое. Поскольку ты хочешь, чтобы реализация (create_impl) не парилась об организации забега по иерархии — вынеси этот забег наружу.


Спасибо, работает.

К>Либо по-простецки, на макросах — просто избавь себя от копипаста

к сожалению в g++ __super или его клон — отсутствует.
Re[4]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 05.10.09 04:50
Оценка:
Здравствуйте, Кодт, Вы писали:

как обойти

class A : public root 
{
    A() {};
};
class B : public derive<A> 
{
    B() : A() {};
}


In constructor ‘B::B()’:
/home/sasha/test/static_factory/static_factory.cpp:41: ошибка: type ‘A’ is not a direct base of ‘B’
Re[5]: Цепочка вызовов перегруженных статических функций
От: Alexander Pazdnikov  
Дата: 05.10.09 05:02
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Здравствуйте, Кодт, Вы писали:


AP>как обойти


Было

class A : public root 
{
    A(int modNum, const string& modType) {};
};
class B : public derive<A> 
{
    B(int modNum, const string& modType) : A(modNum, modType) {};
}


Стало
template <class S> struct derive 
{
    derive(int modNum, const std::string& modType) : S(modNum, modType) {};
    typedef S superclass;
};

class A : public root 
{
    A(int modNum, const std::string& modType) {};
};
class B : public derive<A> 
{
    B(int modNum, const std::string& modType) : derive<A>(modNum, modType) {};
}


Изменения неизбежны ?
Re[6]: Цепочка вызовов перегруженных статических функций
От: Кодт Россия  
Дата: 05.10.09 09:15
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>Изменения неизбежны ?

Ну, в общем, да.

Либо вот так
template<class S> struct declare_superclass { typedef S superclass; };

#define DERIVE(S) public S, public declare_superclass<S>

Макрос нужен, чтобы избежать ошибок копипаста — поскольку одно имя (S) встречается дважды.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.