Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Универсальный конструктор 
V
    Опции темы
Ivan.
Дата 11.12.2015, 13:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 291
Регистрация: 2.11.2004

Репутация: 1
Всего: 2



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

void foo(const Variable<Engine<1000_hertz>> &engine);

Чтобы вызвать данную функцию с передачей объекта нужно написать следующее:
Код

foo(Variable<Engine<1000_hertz>>("Двигатель", 380_volt, MODULE_1_CHANNEL_1));

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

foo(constructor("Двигатель", 380_volt, MODULE_1_CHANNEL_1));

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

Попробуем решить эту задачу. Забегая вперед скажу, что мне это удалось.
Для решения этой задачи нам понадобится универсальная переменная, способная хранить как копии объектов, так и указатели и ссылки. хотя указатель и копия в данном случае одно и тоже, а вот со ссылкой сложнее.
Код

template<typename T>
struct UniversalValue {
    constexpr UniversalValue(T v):v(v){}
    constexpr operator T(){return v;}
private:
    T v;
};
template<typename T>
struct UniversalValue<T&> {
    constexpr UniversalValue(T &v):v(&v){}
    constexpr operator T &(){return *v;}
private:
    T* v;
};

Данный класс позволяет принимать нам любые аргументы.
Все методы имеют атрибут constexpr и могут вычисляться на этапе компиляции.
Здесь все относительно просто, принимаем любой аргумент и сохраняем его копию, а в случае со ссылкой преобразовываем ее в указатель и обратно.
Сама специализация необходима, чтобы отсеять аргументы по ссылке, чтобы создать универсальность, а вот без преобразования в указатель компилятор отказывается выполнять на этапе компиляции, хотя казалось бы в чем разница, храним мы в указателе или в ссылке.

Дальше посложнее. На нужно создать универсальный контейнер с неограниченным числом аргументов произвольных типов:
Код

template<typename Type, typename ...Types>
struct UniversalContainer {
    constexpr UniversalContainer(const Type &&arg, const Types &&...args): arg(arg), args((Types&&)args...){}
    template<typename T> constexpr operator T(){return get<T>();}
private:
    UniversalValue<Type> arg;
    UniversalContainer<Types...> args;
    template<typename T, typename ...Values> T constexpr get(Values &&...v){return args.get<T>((Values&&)v..., arg);}
};

template<typename Type>
struct UniversalContainer<Type> {
    constexpr UniversalContainer(const Type &&arg): arg(arg){}
    template<typename T> constexpr operator T(){return get<T>();}
private:
    UniversalValue<Type> arg;
    template<typename T, typename ...Values> T constexpr get(Values &&...v){return T((Values&&)v..., arg);}
};

Рекурсивный контейнер принимает неограниченное число аргументов различных типов и помещает первый аргумент к себе, а остальные во вложенный контейнер с оставшимися аргументами, пока не дойдем до последнего аргумента.
Также этот контейнер имеет оператор преобразования к любому требуемому типу вызывая рекурсивный метод get с вложением всех имеющихся аргументов.
Все аргументы передаются в качестве rvalue аргументов до самого конечного получателя UniversalValue, чтобы не потерять ссылки.

Ну и наконец сама функция constructor
Код

template<typename ...Types>
constexpr UniversalContainer<Types...> constructor(Types &&...args){return UniversalContainer<Types...>((Types&&)args...);}

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

Ну и напоследок приведу пример:
Код

struct Temp {
    constexpr Temp(int v1, const int &v2, const int* v3): v1(v1), v2(&v2), v3(v3){}
    int v1;
    const int* v2;
    const int* v3;
};
const int a = 10;
const Temp __attribute__((section("text"))) t = constructor(a, a, &a);

В данном примере я передал как копию, так и ссылку и указатель и все это поместилось в кодовую память на этапе компиляции

Добавлено @ 13:50
Немного оптимизировал код вложив UniversalValue в контейнер, чтобы его не было видно
Код

template<typename ...Types>
struct UniversalContainer {
    constexpr UniversalContainer(){}
    template<typename T> constexpr operator T(){return get<T>();}
    template<typename T, typename ...Values> T constexpr get(Values &&...v){return T((Values&&)v...);}
};

template<typename Type, typename ...Types>
struct UniversalContainer<Type, Types...> {
    constexpr UniversalContainer(const Type &&arg, const Types &&...args): arg(arg), args((Types&&)args...){}
    template<typename T> constexpr operator T(){return get<T>();}
    template<typename T, typename ...Values> T constexpr get(Values &&...v){return args.get<T>((Values&&)v..., arg.get());}
private:
    template<typename T>
    struct Value {
        T v;
        constexpr Value(T v):v(v){}
    constexpr T get(){return v;}
    };
    template<typename T>
    struct Value<T&> {
        T* v;
        constexpr Value(T &v):v(&v){}
        constexpr T &get(){return *v;}
    };
    Value<Type> arg;
    UniversalContainer<Types...> args;
};

template<typename ...Types>
constexpr UniversalContainer<Types...> constructor(Types &&...args){return UniversalContainer<Types...>((Types&&)args...);}


Это сообщение отредактировал(а) Ivan. - 14.12.2015, 09:21


--------------------
Я могу ВСЁ, вопрос - сколько времени у меня это займет!
PM MAIL ICQ   Вверх
Cheloveck
Дата 11.12.2015, 13:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

Репутация: 3
Всего: 32





--------------------
user posted image
PM Jabber   Вверх
xvr
Дата 11.12.2015, 15:54 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



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

Хотя и красиво  smile 

PM MAIL   Вверх
Ivan.
Дата 11.12.2015, 16:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 291
Регистрация: 2.11.2004

Репутация: 1
Всего: 2



Велик и могуч сипипишный язык. По истине великую силу получил с++ с введением const expression и variadic template

Это сообщение отредактировал(а) Ivan. - 11.12.2015, 16:51


--------------------
Я могу ВСЁ, вопрос - сколько времени у меня это займет!
PM MAIL ICQ   Вверх
math64
Дата 14.12.2015, 10:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2505
Регистрация: 12.4.2007

Репутация: 8
Всего: 72



Цитата(xvr @  11.12.2015,  15:54 Найти цитируемый пост)
Наглядный пример того, сколько можно написать кода, когда хочется сэкономить пару десятков букв из чистой лени  smile 

А ТС уверен, что код, сгенрированный таким образом не слишком усложнится.

Возможно, в следующих версиях языка мы сможем написать 
Код
foo(auto("Двигатель", 380_volt, MODULE_1_CHANNEL_1));

без всякого усложнения сгенерированного кода.
PM   Вверх
Ivan.
Дата 15.12.2015, 11:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 291
Регистрация: 2.11.2004

Репутация: 1
Всего: 2



 smile Хорошая идея назвать метод auto, только ключевое слово auto уже занято. Не смотря на то, что это метод, и следовало бы назвать его с маленькой буквы, он выполняет роль автоматического конструктора и по этому я решил назвать его с большой буквы Auto. Получается очень лаконично.
Код

template<typename ...Types>
struct Container {
    constexpr Container() {}
    template<typename T> constexpr operator T() {return get<T>();}
    template<typename T, typename ...Values> T constexpr get(Values &&...v) {return T((Values&&)v...);}
};
//---------------------------------------------------------------------------
template<typename Type, typename ...Types>
struct Container<Type, Types...> {
    constexpr Container(const Type &&arg, const Types &&...args): arg(arg), args((Types&&)args...) {}
    template<typename T> constexpr operator T() {return get<T>();}
    template<typename T, typename ...Values> T constexpr get(Values &&...v) {return args.get<T>((Values&&)v..., arg.get());}
private:
    template<typename T>
    struct Value {
        T v;
        constexpr Value(T v): v(v) {}
        constexpr T get() {return v;}
    };
    template<typename T>
    struct Value<T&> {
        T* v;
        constexpr Value(T &v): v(&v) {}
        constexpr T &get() {return *v;}
    };
    Value<Type> arg;
    Container<Types...> args;
};
//---------------------------------------------------------------------------
template<typename ...Types>
constexpr Container<Types...> Auto(Types &&...args) {return Container<Types...>((Types&&)args...);}



--------------------
Я могу ВСЁ, вопрос - сколько времени у меня это займет!
PM MAIL ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0500 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.