История изменений
Исправление KivApple, (текущая версия) :
Я привёл слишком абстрактный пример. Сейчас попробую привести пример конкретнее.
Допустим, я реализую паттерн «стратегия» - есть некий набор алгоритмов с общим интерфейсом и я хочу иметь возможность в другом коде дёргать эти алгоритмы с runtime полиморфизмом. Но это ещё не всё - алгоритмы не простые, а многоэтапные и у алгоритма есть состояние.
Условно:
struct AbstractAlgorithmState {
AbstractAlgorithm &algorithm;
};
class AbstractAlgorithm {
public:
virtual AbstractAlgorithmState *createState() = 0;
virtual void updateState(AbstractAlgorithmState *state) = 0;
virtual void deleteState(AbstractAlgorithmState *state) = 0;
};
struct MyAlgorithmState: public AbstractAlgorithmState {
int a;
}
class MyAlgorithm: public AbstractAlgorithm {
public:
AbstractAlgorithmState *createState() override {
return new MyAlgorithmState { *this, 0 };
}
void updateState(AbstractAlgorithmState *state) override {
auto myState = (MyAlgorithmState*) state;
myState->a++;
std::cout << "A = " << myState->a << std::endl;
}
void deleteState(AbstractAlgorithmState *state) override {
delete (MyAlgorithmState*) state; // Вдруг MyAlgorithmState содержит поля с нетривиальными деструктурами вроде vector или string
}
}
...
AbstractAlgorithmState *state = findAlgorithmByName(...)->createState();
state->algoritm.updateState(state);
state->algoritm.updateState(state);
state->algoritm.updateState(state);
state->algoritm.deleteState(state);
Наследников у AbstractAlgorithm может быть много, методов вроде updateState может быть много. У наследников AbstractAlgorithm могут быть свои поля «глобального состояния», а не всё их состояние хранится в AbstractAlgorithmState. Полиморфизм обязательно нужен в runtime.
Я согласен на любую шаблонную магию, но хочу чтобы написание наследников AbstractAlgorithm было максимально простым и приятным. Требование явного каста всё портит. Хочется, чтобы тип для хранения состояния прописывался в одном месте и больше касты были не нужны (и тогда, например, нельзя выстрелить себе в ногу и создать состояние одного типа, а в функциях обновления работать с другим типом).
Можно было бы вынести обновление состояния в само состояние, но это имеет минус в том, что тогда состояние потолстеет - помимо указателя на экземпляр AbstractAlgorithm (ведь часть состояния, напомню, «глобальная») он будет ещё содержать указатель на vtable. А экземпляров состояний одновременно могут существовать сотни тысяч (при этом у каждого алгоритма состояние очень мало весит - считанные байты) и хочется поэкономить память.
Вариант с шаблонными костылями как я описал в стартовом посте (переопределять в шаблоне виртуальный метод и вызывать другой виртуальный метод, но уже с кастом) приводит к двойному косвенному переходу вместо одного (потеря скорости, а updateState будет вызываться очень-очень много раз).
Исправление KivApple, :
Я привёл слишком абстрактный пример. Сейчас попробую привести пример конкретнее.
Допустим, я реализую паттерн «стратегия» - есть некий набор алгоритмов с общим интерфейсом и я хочу иметь возможность в другом коде дёргать эти алгоритмы с runtime полиморфизмом. Но это ещё не всё - алгоритмы не простые, а многоэтапные и у алгоритма есть состояние.
Условно:
struct AbstractAlgorithmState {
AbstractAlgorithm &algorithm;
};
class AbstractAlgorithm {
public:
virtual AbstractAlgorithmState *createState() = 0;
virtual void updateState(AbstractAlgorithmState *state) = 0;
virtual void deleteState(AbstractAlgorithmState *state) = 0;
};
struct MyAlgorithmState: public AbstractAlgorithmState {
int a;
}
class MyAlgorithm: public AbstractAlgorithm {
public:
AbstractAlgorithmState *createState() override {
return new MyAlgorithmState { *this, 0 };
}
void updateState(AbstractAlgorithmState *state) override {
auto myState = (MyAlgorithmState*) state;
myState->a++;
std::cout << "A = " << myState->a << std::endl;
}
void deleteState(AbstractAlgorithmState *state) override {
delete (MyAlgorithmState*) state; // Вдруг MyAlgorithmState содержит поля с нетривиальными деструктурами вроде vector или string
}
}
...
AbstractAlgorithmState *state = findAlgorithmByName(...)->createState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.deleteState(state);
Наследников у AbstractAlgorithm может быть много, методов вроде updateState может быть много. У наследников AbstractAlgorithm могут быть свои поля «глобального состояния», а не всё их состояние хранится в AbstractAlgorithmState. Полиморфизм обязательно нужен в runtime.
Я согласен на любую шаблонную магию, но хочу чтобы написание наследников AbstractAlgorithm было максимально простым и приятным. Требование явного каста всё портит. Хочется, чтобы тип для хранения состояния прописывался в одном месте и больше касты были не нужны (и тогда, например, нельзя выстрелить себе в ногу и создать состояние одного типа, а в функциях обновления работать с другим типом).
Можно было бы вынести обновление состояния в само состояние, но это имеет минус в том, что тогда состояние потолстеет - помимо указателя на экземпляр AbstractAlgorithm (ведь часть состояния, напомню, «глобальная») он будет ещё содержать указатель на vtable. А экземпляров состояний одновременно могут существовать сотни тысяч (при этом у каждого алгоритма состояние очень мало весит - считанные байты) и хочется поэкономить память.
Вариант с шаблонными костылями как я описал в стартовом посте (переопределять в шаблоне виртуальный метод и вызывать другой виртуальный метод, но уже с кастом) приводит к двойному косвенному переходу вместо одного (потеря скорости, а updateState будет вызываться очень-очень много раз).
Исправление KivApple, :
Я привёл слишком абстрактный пример. Сейчас попробую привести пример конкретнее.
Допустим, я реализую паттерн «стратегия» - есть некий набор алгоритмов с общим интерфейсом и я хочу иметь возможность в другом коде дёргать эти алгоритмы с runtime полиморфизмом. Но это ещё не всё - алгоритмы не простые, а многоэтапные и у алгоритма есть состояние.
Условно:
struct AbstractAlgorithmState {
AbstractAlgorithm &algorithm;
};
class AbstractAlgorithm {
public:
virtual AbstractAlgorithmState *createState() = 0;
virtual void updateState(AbstractAlgorithmState *state) = 0;
virtual void deleteState(AbstractAlgorithmState *state) = 0;
};
struct MyAlgorithmState: public AbstractAlgorithmState {
int a;
}
class MyAlgorithm: public AbstractAlgorithmState {
public:
AbstractAlgorithmState *createState() override {
return new MyAlgorithmState { *this, 0 };
}
void updateState(AbstractAlgorithmState *state) override {
auto myState = (MyAlgorithmState*) state;
myState->a++;
std::cout << "A = " << myState->a << std::endl;
}
void deleteState(AbstractAlgorithmState *state) override {
delete (MyAlgorithmState*) state; // Вдруг MyAlgorithmState содержит поля с нетривиальными деструктурами вроде vector или string
}
}
...
AbstractAlgorithmState *state = findAlgorithmByName(...)->createState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.deleteState(state);
Наследников у AbstractAlgorithm может быть много, методов вроде updateState может быть много. У наследников AbstractAlgorithm могут быть свои поля «глобального состояния», а не всё их состояние хранится в AbstractAlgorithmState. Полиморфизм обязательно нужен в runtime.
Я согласен на любую шаблонную магию, но хочу чтобы написание наследников AbstractAlgorithm было максимально простым и приятным. Требование явного каста всё портит. Хочется, чтобы тип для хранения состояния прописывался в одном месте и больше касты были не нужны (и тогда, например, нельзя выстрелить себе в ногу и создать состояние одного типа, а в функциях обновления работать с другим типом).
Можно было бы вынести обновление состояния в само состояние, но это имеет минус в том, что тогда состояние потолстеет - помимо указателя на экземпляр AbstractAlgorithm (ведь часть состояния, напомню, «глобальная») он будет ещё содержать указатель на vtable. А экземпляров состояний одновременно могут существовать сотни тысяч (при этом у каждого алгоритма состояние очень мало весит - считанные байты) и хочется поэкономить память.
Вариант с шаблонными костылями как я описал в стартовом посте (переопределять в шаблоне виртуальный метод и вызывать другой виртуальный метод, но уже с кастом) приводит к двойному косвенному переходу вместо одного (потеря скорости, а updateState будет вызываться очень-очень много раз).
Исходная версия KivApple, :
Я привёл слишком абстрактный пример. Сейчас попробую привести пример конкретнее.
Допустим, я реализую паттерн «стратегия» - есть некий набор алгоритмов с общим интерфейсом и я хочу иметь возможность в другом коде дёргать эти алгоритмы с runtime полиморфизмом. Но это ещё не всё - алгоритмы не простые, а многоэтапные и у алгоритма есть состояние.
Условно:
struct AbstractAlgorithmState {
AbstractAlgorithm &algorithm;
};
class AbstractAlgorithm {
public:
virtual AbstractAlgorithmState *createState() = 0;
virtual void updateState(AbstractAlgorithmState *state) = 0;
virtual void deleteState(AbstractAlgorithmState *state) = 0;
};
struct MyAlgorithmState: public AbstractAlgorithmState {
int a;
}
class MyAlgorithm: public AbstractAlgorithmState {
public:
AbstractAlgorithmState *createState() override {
return new MyAlgorithmState { *this, 0 };
}
void updateState(AbstractAlgorithmState *state) {
auto myState = (MyAlgorithmState*) state;
myState->a++;
std::cout << "A = " << myState->a << std::endl;
}
void deleteState(AbstractAlgorithmState *state) override {
delete (MyAlgorithmState*) state; // Вдруг MyAlgorithmState содержит поля с нетривиальными деструктурами вроде vector или string
}
}
...
AbstractAlgorithmState *state = findAlgorithmByName(...)->createState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.updateState();
state->algoritm.deleteState(state);
Наследников у AbstractAlgorithm может быть много, методов вроде updateState может быть много. У наследников AbstractAlgorithm могут быть свои поля «глобального состояния», а не всё их состояние хранится в AbstractAlgorithmState. Полиморфизм обязательно нужен в runtime.
Я согласен на любую шаблонную магию, но хочу чтобы написание наследников AbstractAlgorithm было максимально простым и приятным. Требование явного каста всё портит. Хочется, чтобы тип для хранения состояния прописывался в одном месте и больше касты были не нужны (и тогда, например, нельзя выстрелить себе в ногу и создать состояние одного типа, а в функциях обновления работать с другим типом).
Можно было бы вынести обновление состояния в само состояние, но это имеет минус в том, что тогда состояние потолстеет - помимо указателя на экземпляр AbstractAlgorithm (ведь часть состояния, напомню, «глобальная») он будет ещё содержать указатель на vtable. А экземпляров состояний одновременно могут существовать сотни тысяч (при этом у каждого алгоритма состояние очень мало весит - считанные байты) и хочется поэкономить память.
Вариант с шаблонными костылями как я описал в стартовом посте (переопределять в шаблоне виртуальный метод и вызывать другой виртуальный метод, но уже с кастом) приводит к двойному косвенному переходу вместо одного (потеря скорости, а updateState будет вызываться очень-очень много раз).