А что делать, если getfromconfig создаёт объект, а использовать его надо через какое-то время?
Сейчас, насколько я понял, мы не теряем информацию о типе создаваемого getfromconfig объекта.
Здравствуйте, tdiff, Вы писали:
T>А что делать, если getfromconfig создаёт объект, а использовать его надо через какое-то время? T>Сейчас, насколько я понял, мы не теряем информацию о типе создаваемого getfromconfig объекта.
Очевидно, что любая ленивость и любое откладывание на неопределённое "потом" переводит полиморфизм в рантайм.
Единственный вариант чисто статического полиморфизма — это весь main() пересадить на рельсы CPS. В принципе, это возможно.
Компромиссное решение — это сделать некоторое статически параметризованное окружение, т.е. шаблон класса, например, — у которого будет виртуальная точка входа.
// былоclass Environment
{
Base* x;
Base* y;
void one(char cx) { x = getconfig(cx); }
void two(char cy) { y = getconfig(cy); }
void three()
{
..... здесь гора всякого полезного кода .....
}
.....
};
// сталоstruct IEnvironment
{
IEnvironment* one(char cx);
virtual IEnvironment* two(char cy) = 0;
virtual IEnvironment* three() = 0;
};
template<class X, class Y> // чтобы компилировалось, нужно начать с каких-то null-объектов - заглушек (например)class Environment : public IEnvironment
{
X x;
Y y;
virtual IEnvironment* one(char cx) override
{
IEnvironment* res = nullptr;
getconfig(cx, [&](auto x) { res = new Environment<decltype(x),Y>(); res->x = x; });
return res;
}
virtual IEnvironment* two(char cy) override
{
IEnvironment* res = nullptr;
getconfig(cy, [&](auto y) { res = new Environment<X,decltype(y)>(); res->y = y; });
return res;
}
virtual void three() override
{
..... код, конкретизированный для X,Y .....
}
.....
};
И завернуть всё это хозяйство в идиому pimpl и паттерн State.
Здравствуйте, Tilir, Вы писали:
Не очень понял, причём тут конструктор, но
class PredicateExample {
public:
static int Do(int param) {return 0;}
};
class BaseObj {
public:
typedef int (*Function)(int);
BaseObj(Function fu) : foo_address(fu) {}
int foo(int param) {return foo_address(param);}
protected:
void set_foo_address(Function fu) {foo_address = fu;}
private:
Function foo_address;
};
template <class Predicate>
class Obj : public BaseObj
{
public:
Obj() : BaseObj(Predicate::Do) {}
};
class APredicate;
class BPredicate;
class A : public Obj<APredicate> {};
class B : public Obj<BPredicate> {};
//...
/* returns A is config have 'a', B is 'b', and so on */
BaseObj * getfromconfig (const char *filename);
int entry (BaseObj *x)
{
return x->foo(3);
}
int main (void)
{
return entry (getfromconfig("my.xml"))
}
Мораль тут в том, что виртуальность — это такой хитрожопый способ задать сишный указатель на функцию, поэтому с помощью указателя на функцию его всегда легко можно сымитировать. А уж чем имитировать — статическим полиморфизмом или просто на си фигарить тоже самое, второй вопрос. Можно даже на статическом полиморфизме наваять класс, который будет имитировать таблицу виртуальных функций. Только зачем?
Здравствуйте, Molchalnik, Вы писали:
M>Не очень понял, причём тут конструктор, но
Это ещё одна прекрасная реализация vtbl руками, которых набралось уже много в этом треде. Она скорее доказывает, что динамический полиморфизм существенно необходим, раз уж приходится его реализовывать хакерскими способами при попытке обойтись шаблонами.
Впрочем, решение уважаемого Кодт'а (пожалуй лучшее в этом треде) показывает забавный способ вывернуть абстракцию.
Здравствуйте, Tilir, Вы писали:
T>Возник теоретический спор об эквивалентности шаблонного и динамического полиморфизма в C++ T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?
Шаблонный полиморфизм — статический. А статическому полиморфизму — статические параметры.
Я бы переписал так:
Здравствуйте, Tilir, Вы писали:
T>Здравствуйте, Molchalnik, Вы писали:
M>>Не очень понял, причём тут конструктор, но
T>Это ещё одна прекрасная реализация vtbl руками, которых набралось уже много в этом треде. Она скорее доказывает, что динамический полиморфизм существенно необходим, раз уж приходится его реализовывать хакерскими способами при попытке обойтись шаблонами.
Дружище, давай не будем мешать тёплое с мягким. В твоём мире может быть всё, что угодно, ты в нём хозяин, но если речь идёт об объективной действительности, то нужно руководствоваться законами этой действительности, в частности, логикой. Ты дал два противоречивых запроса — "как сделать ЭТО без виртуальных функций" и "Проверить эквивалентность динамического и статического полиморфизма". Эти две вещи никак не связаны . Что с того, если этот пример нельзя реализовать без vtbl? Это не означает "неэквивалентности", ты исходишь из ложной посылки, имеешь мусор на входе, получишь мусор на выходе. К тому же, если следовать твоей логике, то 90% использований указателей на функции будут "попытками имитации vtbl". Не кажется ли тебе, что это бред?
Второе, динамический полиморфизм — это принцип программирования, а не фича языка, а не важно, на чём и как его делать, хоть на ассемблере.
Эквивалентнось полиморфизмов означает, что любой запрос юзера или любой алгоритм может быть реализован первым или вторым способом. Проблема твоего фрейма (=твоего восприятия реальности) в том, что ты под эквивалентностью понимаешь возможность не повторения алгоритма, а повторения метода реализации этого алгоритма. Кто-то интуитивно поймал эту идею и попытался тебе объяснить, но не очень удачно.
Если формализовать твой запрос (именно тот, который выражен через пример), перевести его на язык логики, то получится, "можно ли реализовать динамический полиморфизм без динамического полиморфизма". И получаешь соответственные ответы. Но если ответ НЕТ , то это не означает неэквивалентности, просто означает то, что динамический полиморфизм нельзя реализовать без динамического полиморфизма. Впрочем, насчёт последнего не уверен, ещё подумаю.
Здравствуйте, Molchalnik, Вы писали:
M> руководствоваться законами этой действительности, в частности, логикой
Не понял сути предъяв за логику и перехода на личности. Есть спорный тезис, что статический полиморфизм (на шаблонах) эквивалентен динамическому (на виртуальных функциях).
Разумеется и тот и другой полиморфизмы могут быть фейкнуты в C стиле -- я могу сделать внешний генератор вместо шаблонов или реализовать vtbl руками. Точно так же я вообще могу написать любой код на ассемблере и что?
Просто, если без подтасовок не обойтись, значит этот тезис для C++ неверен, всего-то. Скажем легко проиллюстрировать что указатели на C++ мощнее ссылок -- достаточно написать на них стек. А вот со статическим/динамическим полиморфизмом даже виртуальный конструктор оказывается недостаточно убедителен как пример.
И главное, я ведь не защищаю и не опровергаю этот тезис, а лишь ищу (и нахожу, как показывает тред) на форуме интересные идеи на тему. Ваше участие я оценил (см. оценку на первом посте). Если по делу больше сказать нечего, я готов признать себя нелогичным и т.п. Мы тут не в КСВ, так что я предлагаю обсуждать по существу.
Здравствуйте, Molchalnik, Вы писали:
M>Эквивалентнось полиморфизмов означает, что любой запрос юзера или любой алгоритм может быть реализован первым или вторым способом. Проблема твоего фрейма (=твоего восприятия реальности) в том, что ты под эквивалентностью понимаешь возможность не повторения алгоритма, а повторения метода реализации этого алгоритма. Кто-то интуитивно поймал эту идею и попытался тебе объяснить, но не очень удачно.
Поскольку статический полиморфизм должен отработать вширь на стадии компиляции, — почти любая задача, интенсивно эксплуатирующая множество полиморфных объектов, взорвёт компиляцию.
Однако, есть задачи, которые можно переделать в статику. Разбор конфигов — одна из таких.
Кстати, с конфигами это не шутки. У меня нечто похожее:
template<
class AcousticModelType, // 2 типа акустических моделей, - грузятся из файла (выбранного в конфиге)class GraphType, // 3 формата графов переходов, - грузятся из бинарного файла (выбранного в конфиге)class OutputLatticeType // 2 типа выходной сетки гипотез, - выбираются в конфиге
>
class SpeechDecoderImpl { ..... };
Поскольку это адская числодробилка, которая должна быстро запускаться и быстро вертеться, — то всё, что можно, избавлено от виртуальных вызовов и даже от лишних указателей.
Здравствуйте, Tilir, Вы писали:
T>Здравствуйте, Molchalnik, Вы писали:
M>> руководствоваться законами этой действительности, в частности, логикой
T>Не понял сути предъяв за логику и перехода на личности.
Перехода на личности не было.
Был логический разбор твоего примера с точки зрения провозглашённой тобой цели
T> Есть спорный тезис, что статический полиморфизм (на шаблонах) эквивалентен динамическому (на виртуальных функциях).
Эквивалентен в общем случае. И что? Как это опровергает твой пример? Мой тезис — никак. Считаю, и обосновал, что твой пример не связан с этим тезисом. Не подтверждает и не опровергает его.
T>Просто, если без подтасовок не обойтись, значит этот тезис для C++ неверен, всего-то.
Не согласен. Есть некая подмена понятий. "Можно ли любой динамический полиморфный код заменить на статический?" — ответ нет, и твой пример это иллюстрирует хорошо. "Эквивалентны ли динамический и статический полиморфизм?" — Ответ да, с точностью до константы.
Можно спорить о границах этой эквивалентности. Но отрицать её — это всё равно что при кодеревью идеального кода усмотреть один пробел в конце одной строки и за это поставить двойку. Необоснованный формализм.
Придраться к этому тезису можно, а опровергнуть дельно — нет.
Так же у тебя, по моему мнению, отсутствует понимание того, что замена одного метода на другой должна вызывать смену подхода. Т.е. замена динамического полиморфизма статическим — это не десяток мест в коде переписать. Это весь код выкинуть на свалку и написать взамен него другой, с другой архитектурой. Имхо, эквивалентность — это когда с помощью одного подхода построить любой алгоритм, который решает другой подход. Ты же считаешь, что эквивалентность — это когда любую строчку кода на динамическом полиморфизме можно заменить другой строчкой кода на статическом полиморфизме. А это, имхо, искажение понятия эквивалентности.
T>И главное, я ведь не защищаю и не опровергаю этот тезис, а лишь ищу (и нахожу, как показывает тред) на форуме интересные идеи на тему. Ваше участие я оценил (см. оценку на первом посте). Если по делу больше сказать нечего, я готов признать себя нелогичным и т.п. Мы тут не в КСВ, так что я предлагаю обсуждать по существу.
Здравствуйте, Tilir, Вы писали:
T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?
T>
T>/* returns A is config have 'a', B is 'b', and so on */
T>obj * getfromconfig (const char *filename);
T>
T>int
T>entry (obj *x)
T>{
T> return x->foo();
T>}
T>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Molchalnik, Вы писали:
К>Поскольку статический полиморфизм должен отработать вширь на стадии компиляции, — почти любая задача, интенсивно эксплуатирующая множество полиморфных объектов, взорвёт компиляцию.
Но ведь это проблемы компилятора и ресурсов компилирующей машины, а не тезиса об эквивалентности, верно?
Любая задача, решаемая в статике, может быть решена и в динамике. Просто бремя расчётов будет перенесено с real-time исполнения на процесс компиляции. Именно на этом базируется тезис (правильнее сказать, доказанная теорема) об эквивалентности статического и динамического полиморфизма.
Я бы сформулировал проблему статики иначе. Обратите внимание, все попытки опровергнуть тезис об эквивалентности построены на внешних данных, неизвестных на этапе компиляции. Других способов построить "опровержение" тезиса об эквивалентности нет и быть не может.
Так вот, с точки зрения математики, любой алгоритм, переводящий одни данные в другие может быть решён как в статике, так и в динамике. И разницы в том, как он будет решён, нет никакой — просто часть расчётов переносится на этап компиляции и код пишется под это. Более того, грамотная статика по определению эффективнее динамики (ибо часть расчётов производится на этапе компиляции, а так же происходит оптимизация общего кода под частный случай). Поэтому если стать на позицию своих оппонентнов, противников эквивалентности, то я бы признал то, что опровергнуть невозможно — да, математическая эквивалентность дескать есть, но нам, программистам, с этого нет никакого навара, потому что задачи, привязанные ко времени, статический полиморфизм не решит. С математической точки зрения это неправда, т.к. можно ввести время как один из параметров. А на практике — верно, т.к. машина не может предсказать все варианты ввода данных от пользователя и внешних устройств, и в зависимости от них построить код "на все случаи жизни" и рассчитать вывод для каждой секунды времени с момента запуска. Просто утонет в количестве комбинаций. Но теоретически — это возможно, поэтому теоретически статика и динамика абсолютно эквивалентны, а на практике — у каждой свои преимущества. Там, где статика применима при прямых руках она сделает динамику по скорости, иногда в несколько раз.
Здравствуйте, Molchalnik, Вы писали:
К>>Поскольку статический полиморфизм должен отработать вширь на стадии компиляции, — почти любая задача, интенсивно эксплуатирующая множество полиморфных объектов, взорвёт компиляцию. M>Но ведь это проблемы компилятора и ресурсов компилирующей машины, а не тезиса об эквивалентности, верно?
Теоретически, между теорией и практикой нет разницы. Практически, разница есть.
M>Любая задача, решаемая в статике, может быть решена и в динамике. Просто бремя расчётов будет перенесено с real-time исполнения на процесс компиляции. Именно на этом базируется тезис (правильнее сказать, доказанная теорема) об эквивалентности статического и динамического полиморфизма.
Статику в динамику перевести — дело нехитрое. Динамику в статику — это другое дело.
Если мы исходим из того, что машина, на которой будет исполняться динамика, — конечная, то можно перечислить все её состояния и захардкодить граф переходов.
Как честно захардкодить бесконечный автомат?
Причём речь даже не о полноценном вход-выходовом преобразователе, где на вход поступает потенциально бесконечная цепочка.
Есть ещё и такая фигня, как проблема останова.
Пусть программа на вход получает конечное множество данных и выдаёт ответ в конечной области значений, дополненной значением "ой-зациклилось".
Множество таких функций, очевидно, тоже конечное: M^N, где N — мощность входов, M — мощность выходов (включая ой).
Беда в том, что любую функцию можно описать неограниченным количеством способов.
Если компилятору не дать подсказку, где лежат грабли, то мы рискуем никогда не построить табличное представление функции.
Внешних состояний всего N, а внутренних — бесконечность.
Здравствуйте, Tilir, Вы писали: T>Hi, T>Возник теоретический спор об эквивалентности шаблонного и динамического полиморфизма в C++ T>Был предложен такой контрпример тезису об эквивалентности как виртуальный конструктор:
Скрытый текст
T>
T>class obj
T>{
T>public:
T> virtual int foo (int) = 0;
T>};
T>class A : public obj;
T>class B : public obj;
T>...
T>/* returns A is config have 'a', B is 'b', and so on */
T>obj * getfromconfig (const char *filename);
T>int
T>entry (obj *x)
T>{
T> return x->foo();
T>}
T>int
T>main (void)
T>{
T> return entry (getfromconfig("my.xml"))
T>}
T>
T>Как бы вы переписали этот код с шаблонным полиморфизмом вместо виртуальных функций?
несмотря на некорректность этого куска кода как контрпримера, переписать его можно просто, если не заморачиваться на копирование действий программы, а просто реализовать нужный алгоритм:
//class obj { public: virtual int foo (int) = 0; };class A {//: public obj {public:
int foo() {return (int)'a';}
};
class B {//: public obj {public:
int foo() {return (int)'b';}
};
template <class Object>
class Config1 {
public:
Object GetFrom (const char *filename) {return Object();}
};
template <class Object>
class Config2 {
public:
Object GetFrom (const char *filename) {return Object();}
};
template <template<typename> class Config, class Object>
int entry(const char * filename, Config<Object> & config) {
return config.GetFrom(filename).foo();
}
int main() {
Config1<A> config;
return entry("my.xml",config);
}
Предупреждая возражения "так в результате разбора конфига создаётся полиморфный объект, и он может быть абсолютно различным".
Да. В исходном примере создаётся. Но ведь задача избавится от динамического полиморфизма? Значит, в результате разбора конфига должен создаваться только один, неполиморфный объект, с функционалом достаточным, чтобы включать все возможные результаты разбора конфига. Это всегда возможно.
Кстати, если не ограничиватся обычным С++, то можно (легко) на 11х плюсах реализовать подлейший приём, который полностью повторит функционал динамического полиморфизма, однако, назвать его "имитацией динамики" не выйдет Но это попозже, сейчас много работы.
Здравствуйте, Кодт, Вы писали:
К>Статику в динамику перевести — дело нехитрое. Динамику в статику — это другое дело. К>Если мы исходим из того, что машина, на которой будет исполняться динамика, — конечная, то можно перечислить все её состояния и захардкодить граф переходов. К>Как честно захардкодить бесконечный автомат?
Если возможности "статики" полны по тьюрингу, то без проблем. Просто компиляция будет очень-очень долгой и памяти должно быть достаточно для данной задачи.
Здравствуйте, Molchalnik, Вы писали:
К>>Если мы исходим из того, что машина, на которой будет исполняться динамика, — конечная, то можно перечислить все её состояния и захардкодить граф переходов. К>>Как честно захардкодить бесконечный автомат?
M>Если возможности "статики" полны по тьюрингу, то без проблем. Просто компиляция будет очень-очень долгой и памяти должно быть достаточно для данной задачи.
Ну вот хорошие макропроцессоры и развитые системы типов полны по Тьюрингу.
Это значит, что мы можем зависнуть не только при запуске программы, но и при её компиляции.
Не просто отожрать крендельон памяти, а честно зависнуть.
Здравствуйте, Кодт, Вы писали:
К>Ну вот хорошие макропроцессоры и развитые системы типов полны по Тьюрингу. К>Это значит, что мы можем зависнуть не только при запуске программы, но и при её компиляции. К>Не просто отожрать крендельон памяти, а честно зависнуть.