LINUX.ORG.RU

Шаблонное программирование в C++ и хвостовая рекурсия


0

3

Есть ли смысл при программировании на шаблонах применять хвостовую рекурсию?

Например, что лучше:

template <int n>
struct Fac
{
    enum { result = n * Fac<n - 1>::result };
};

template <>
struct Fac<0>
{
    enum { result = 1 };
};

или

template <int n>
class TailFac
{
    template <int counter, int mul>
    struct TailFacIter
    {  
        enum { value = TailFacIter<counter - 1, mul * counter>::value };
    };

    template <int mul>
    struct TailFacIter<0, mul>
    {  
        enum { value = mul };
    };

public:
    enum { result = TailFacIter<n - 1, n>::value };
};

Даст ли второй подход меньшее потребление памяти и большую скорость компиляции?

Ответ на: комментарий от anonymous

>Если бы shty не стал выебы*ваться как дурачек на первых страницах, все было бы намного интереснее.

Первый Александреску начал вы*бываться. Кто-то в своих проектах юзает его списки типов?

anonymous
()
Ответ на: комментарий от anonymous

> Первый Александреску начал вы*бываться. Кто-то в своих проектах

юзает его списки типов?


Я юзал. Они меня выручили просто потрясающим образом.

archimag ★★★
()
Ответ на: комментарий от archimag

>Я юзал. Они меня выручили просто потрясающим образом.

Расскажешь подробнее? Совершенно не понимаю, зачем они могут понадобиться.

anonymous
()
Ответ на: комментарий от anonymous

> Совершенно не понимаю, зачем они могут понадобиться.

Была дана иерархия классов, с корнем с неким базовым классом. Она была жёсткой и на неё повлиять я не мог . Мне надо было расшрирять эту иерархию, добавляя свои классы, каждый из которых наследовал от какого-то из классов базовой иерархии (не от одного и того же, а от разных). Для моих классов было несколько интерфейсов, которые они реализовывали. Но выразить эти интерфейсы стандартными средствами было невозможно (множественное наследование ломало механизм сериализации). Так что приходилось использовать некое подобие «утиной типизации» - получить объект, проверить что он принадлежит к классу, который реализует нужный интерфейс, привести указатель к настоящему типу и вызывать нужный метод.

Моих классов было несколько десятков и методов из моих интерфейсов тоже было около 20. Так что прямолинейная реализация этого путем RTTI и множества if-ов жутко взрывало размер кода.

Поэтому, я объединял классы, реализующий заданный интерфейс в список типов. Теперь, для вызова метода myMethod в объекте вызывал шаблонную функцию myMethod, которой передавал список типов и указатель на объект. Эта шаблонная функция на основе списка типов рекурсивно раскрывалась в код, который находил класс, точно совпадающий с классом объекта, приводила указатель к указателю на этот класс и вызывал нужный метод.

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

archimag ★★★
()
Ответ на: комментарий от archimag

Или неправильно понял задачу или почему обычными type_traits нельзя было обойтись. Завести тип, в котором хранить информацию о возможности приведения к интерфейсам и настраивать его для классов (специализировать или просто внутрь класть typedef'ом)

anonymous
()
Ответ на: комментарий от aho

> СУБД - от больших до малых С и С++ рулят во все поля браузеры - смотри СУБД

Почитай сравнение git vs monotone. Второе - унылое, тормозное г-но на С++, настолько тормозное, что использовать на относительно средних деревьях - вроде ядра линукс - уже нельзя. Именно из-за тормозов и было принято решение городить свою систему - git

anonymous
()
Ответ на: комментарий от anonymous

> Почитай сравнение git vs monotone. Второе - унылое, тормозное г-но на С++, настолько тормозное, что использовать на относительно средних деревьях - вроде ядра линукс - уже нельзя. Именно из-за тормозов и было принято решение городить свою систему - git

да, я знаю, что на С и С++ тоже можно написать медленную программу

aho
()
Ответ на: комментарий от archimag

> Значит, инженеры <...> Apple <...> насколько сильно любят геймдев, что притащили оттуда С++ в свои ключевые проекты? Вот оно как.

Ты не лопнешь, детка? Apple - это фанаты ObjectiveC. Максимум, что они делают - это «притаскивают» (т.е. берут и допиливают) уже готовые проекты на C++ (как было с khtml/WebKit)

anonymous
()
Ответ на: комментарий от anonymous

> обычными type_traits нельзя было обойтись.

Не понял предлагаемого решения. Есть объект, на который доступен указатель типа A*, но реальный тип объекта - B. Необходимо привести указатель к истинному типу (B*) и вызвать у него конкретный метод. Классов таких (по типу B) - несколько десятков, методов которые надо так вызывать - тоже много.

archimag ★★★
()
Ответ на: комментарий от archimag

> Необходимо привести указатель к истинному типу (B*) и вызвать у него конкретный метод.

что-то вроде: QI( p, B )->func(); ? как в COM?

aho
()
Ответ на: комментарий от anonymous

> Apple - это фанаты ObjectiveC.

как было с khtml/WebKit


WebKit это не мелочь какая-нибудь и объём работы под существенный. Поэтому, это особенно показательно, что эти инженеры забыли о своих предпочтениях и выбрали решение на базе С++ для реализации такого важного компонента.

archimag ★★★
()
Ответ на: комментарий от aho

> что-то вроде: QI( p, B )->func(); ?

Да, только B неизвестен. Есть объект, он может быть представителем одного из 20-ти классов, надо привести к тому, которому он реально соответствует и вызвать метод.

как в COM?


Я COM последний раз трогла больше 5-ти лет назад, не помню уже как там. Но вроде не так.

archimag ★★★
()
Ответ на: комментарий от archimag

А инженеры Facebook и Vkontakte настолько сильно любят сайты-визитки, что притащили оттуда PHP в свои ключевые проекты. Вот так вот.

anonymous
()
Ответ на: комментарий от archimag

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

anonymous
()
Ответ на: комментарий от archimag

> WebKit это не мелочь какая-нибудь и объём работы под существенный. Поэтому, это особенно показательно, что эти инженеры забыли о своих предпочтениях и выбрали решение на базе С++ для реализации такого важного компонента.

Покупатель может выбрать любой цвет машины, при условии, что этот цвет - чёрный (С) Форд

У них не было выбора, это раз. Второе - все свои приложения пишут на Obj C

anonymous
()
Ответ на: комментарий от anonymous

> Я как бы показываю, что отраслью правят студенты-недоучки,

дилетанты и маркетологи.


Э, не понял, как именно вы это показываете?

эзотерическими язычками


Это вы про C++ и PHP? Или про что вообще?

archimag ★★★
()
Ответ на: комментарий от anonymous

> У них не было выбора, это раз

Как же, он не только был, но они и его и делали.

archimag ★★★
()
Ответ на: комментарий от archimag

> Это вы про C++ и PHP? Или про что вообще?

Он про то, что в жизни выбирают нынче Java/PHP ( и Delphi/VB в годы оны), а не окамль с хаскеллем.

LamerOk ★★★★★
()
Ответ на: комментарий от archimag

Видимо, чего-то не понимаю я, потому что не вижу проблемы. Можно так:

template< typename F, typename T > T * try_cast( F * obj ){ return 0; }
А правила приведения задавать специализацией:
template<> B * try_cast< A, B >( A * obj ) { return( B * )obj; }
Теперь, можно вызывать методы:
template< typename C, typename I > void quack( C * obj )
{
  I * iface = try_cast< C, I >( obj );
  if( iface != 0 ) iface->quack();
}
Получается многословно, если интерфейсов и классов много. Если определенные классы принадлежат нескольким интерфейсам B1 и B2, то информацию об этом можно объединить в одну структуру:
struct yes { };
struct no { };

template< typename T > struct ifaces { typedef no result; };
template<> struct ifaces< B1 > { typedef yes result; };
template<> struct ifaces< B2 > { typedef yes result; };
Теперь введем операцию implements и определим, что класс A, реализует интерфейсы ifaces:
template< typename C, typename I > struct implements { };
template< typename I > struct implements< A, I > : public ifaces< I > { };
Теперь операция try_cast:
template< typename C, typename I > I * try_cast_aux( C * obj, no ){ return 0; }
template< typename C, typename I > I * try_cast_aux( C * obj, yes ){ return (I*)obj; }
template< typename C, typename I > I * try_cast( C * obj )
{
    return try_cast_aux<C, I >( obj, typename implements< C, I >::result() );
}
Ну и методы вызываются, как в предыдущем примере.

anonymous
()
Ответ на: комментарий от LamerOk

> Он про то, что в жизни выбирают нынче Java/PHP ( и Delphi/VB в годы оны), а не окамль с хаскеллем.

У вас какая-то альтернативная жизнь.

anonymous
()
Ответ на: комментарий от anonymous

> Видимо, чего-то не понимаю я

Хм, я уже больше двух лет не писал на С++, так что с трудом читаю такой код. Давайте ещё раз.

Есть указатель на объект. У этого объекта надо вызвать метод XXX. Метод XXX не наследуется от какого-либо базового класса, но объявлен в конкретных 20-ти классах. Необходимо проверить принадлежит ли объект одному из этих 20-ти классов и если принадлежит, то привести указатель к типу указателя на найденный класс и вызвать у него метод XXX.

Ваш код позволяет это делать? Я этого не увидел. Ибо здесь вообще не используется RTTI, что совершенно необходимо.

archimag ★★★
()
Ответ на: комментарий от aho

> да, я знаю, что на С и С++ тоже можно написать медленную программу

Ну конечно, стандартная отмазка. Если тормозит программа на жабе, то виновата жаба, а если тормозит программа на с++ - это виноват программист.

anonymous
()
Ответ на: комментарий от anonymous

> Ну конечно, стандартная отмазка. Если тормозит программа на жабе, то виновата жаба, а если тормозит программа на с++ - это виноват программист.

это не отмазка, программы на С и С++ действительно тормозят меньше, если в этом «виноваты» программисты - пусть будет так

aho
()
Ответ на: комментарий от anonymous

> Видимо, чего-то не понимаю я, потому что не вижу проблемы.

Мой телепатический модуль подсказывает мне, что ты не понимаешь того, что архимаг определял тип объекта во время выполнения, а не компиляции.

LamerOk ★★★★★
()
Ответ на: комментарий от archimag

Угу, мы с аноном подрабатываем на полставки.

LamerOk ★★★★★
()
Ответ на: комментарий от aho

> программы на С и С++ действительно тормозят меньше, если в этом «виноваты» программисты - пусть будет так

А почему в этом виноваты программисты?

anonymous
()

Всем спасибо за помощь, проблема решилась перекомпиляцией glib с выключенной оптимизацией (пришлось править Makefile, сгенерированный Автотулзами). Костыли-костылями, зато бесплатно (C)!

zombiegrinder_6000
() автор топика
Ответ на: комментарий от zombiegrinder_6000

Всем спасибо за помощь, проблема решилась перекомпиляцией glib с выключенной оптимизацией (пришлось править Makefile, сгенерированный Автотулзами). Костыли-костылями, зато бесплатно (C)!

Вы точно тему не перепутали? :)

shty ★★★★★
()
Ответ на: комментарий от LamerOk

>Мой телепатический модуль подсказывает мне, что ты не понимаешь того, что архимаг определял тип объекта во время выполнения, а не компиляции.

Стало понятнее, хотя рантайма побаиваюсь.

anonymous
()
Ответ на: комментарий от archimag

Есть указатель на объект. У этого объекта надо вызвать метод XXX. Метод XXX не наследуется от какого-либо базового класса, но объявлен в конкретных 20-ти классах. Необходимо проверить принадлежит ли объект одному из этих 20-ти классов и если принадлежит, то привести указатель к типу указателя на найденный класс и вызвать у него метод XXX.

В принципе, здесь действительно список типов оптимален, по-моему. Но можно и по-другому попробовать. Пусть у нас есть базовый класс и два интерфейса с методом «qua»:

struct base { virtual ~base() { } };
struct iface1 : base { void qua() { puts( "qua 1" ); } };
struct iface2 : base { void qua() { puts( "qua 2" ); } };
Определим класс для хранения набора интерфейсов, на принадлежность которым будем проверять объект:
template< class T1 = void, class T2 = void > struct set { };
typedef set< iface1, iface2 > ifaces;
Теперь можно написать код, который пробегается по набору интерфейсов и дергает функцию, если нашел нужный:
template< class T1, class T2 > struct invoker {
    static void qua( base * obj ) {
        T1 * iface = dynamic_cast< T1 * >( obj );
        if( iface != 0 ) iface->qua();
        else invoker< T2, void >::qua( obj );
    }
};

template<> struct invoker< void, void > {
    static void qua( base * obj ) { }
};
И вспомогательную функцию, чтобы каждый раз список типов вручную не указывать:
template< typename T1, typename T2 > void
qua( base * obj, set< T1, T2 > ) {
    invoker< T1, T2 >::qua( obj );
}
Теперь такой код должен квакать правильно:
base * obj1 = new iface1;
base * obj2 = new iface2;
base * obj3 = new base;

qua( obj1, ifaces() );
qua( obj2, ifaces() );
qua( obj3, ifaces() );

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.