LINUX.ORG.RU

Плюсы как скриптовый язык (вывод констант во время компеляции)

 , , , ,


0

5

Доброе утро!

Захотелось поиграться с шаблонами, наваял для начала такой код:

#include <iostream>

#ifndef X
#error usage: c++ --std=c++11 -DX=<argument> __FILE__ && ./a.out
#endif // X

using xy_type = unsigned long long;

template <xy_type x>
struct fact {
  xy_type y = x * fact<x-1>().y;
};

template <>
struct fact<0> {
  xy_type y = 1;
};

int main() {
  std::cerr << fact<X>().y << std::endl;
  return 0;;
}


Всё хорошо, но не достигнута одна цель: я хочу выводить fact<X>().y во время компиляции, желательно роняя её, чтобы не произвести бинарь. Типа как при #error. Не получается, пытался сделать что-то с макросами и ещё чем-то, толку нет. Ввод организовать легко, через -D, а с выводом придумать не могу. Есть идеи?

P.S. Туплю, но ещё маленький вопрос: почему __FILE__ не подставляется?

P.S. Туплю, но ещё маленький вопрос: почему __FILE__ не подставляется?

Потому что он не раскрывает макросы?

пытался сделать что-то с макросами

Этапы разные. После препроцессора никаких макросов не остаётся.

a1batross ★★★★★
()

Да, кстати. Принимается любое решение, в порядке убывания:


  • Что-то из плюсовых функций, типа std::enable_if, обрущащих компиляцию многословно, но чтобы ответ был (к примеру, static_assert(false, «как-то интерполированная сюда константа, идеально — с сообщением вместе»);
  • Макросы (пусть даже большие);
  • Компиляторо-зависимые штуки
evilface ★★
() автор топика
Ответ на: комментарий от a1batross

Я понимаю. Но #error срабатывает во время препроцессинга же.

evilface ★★
() автор топика

Г-ди, что за изврат, ну да ладно, отвечу по пунктам 1. https://github.com/mikael-s-persson/templight - тебе в эту сторону копать 2. Макросы не рекурсивны. __FILE__ уже определен как макрос, а значит не может использоваться как часть макроса (точнее препроцессор не будет повторно искать чему равен __FILE__ после того как он один раз подставил его в твой код)

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

Блин, про нерекурсивность макросов реально забыл после темплейтов. Действительно «туплю».

За ссылку спасибо.

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

На тебе костыльное решение для данного случая


constexpr void err_getter() {
  std::integral_constant<xy_type, fact<X>().y> err =
      std::integral_constant<xy_type, fact<X + 1>().y>();
}

int main() {
  err_getter();
  return 0;
}

У меня при X = 2 вывело «ошибка: запрошено преобразование от «std::integral_constant<long long unsigned int, 6>» к нескалярному типу «std::integral_constant<long long unsigned int, 2>»»

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

Извращаюсь дальше, теперь более общий случай

template <class T, T value> struct holder { holder(holder<T, value> &&val); };

constexpr void err_getter() { holder<decltype(fact<3>().y), fact<3>().y>(); }

Выводит:

izvrat/main.cpp:11:74: ошибка: no matching function for call to «holder<long long unsigned int, 6>::holder()»

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

Вау, круто. Правда, циферка в ответе была только в gcc, в шланге не смогло. Ну и ладно.

Вообще, как я понимаю, происходит тут следующее: делаем структуру, принимающую тип и значение этого типа, у которой только один конструктор перемещения, остальные удалились.

Дальше функция, где конструируем объект с удалённым конструктором по-умолчанию, а gcc конкретизирует тип (вычислив для этого факториал) и падает из-за отсутствия конструктора, в сообщении выводя, что нет конструктора уже у конкретного типа, параметризованного решением.

Я правильно понимаю?

В принципе, такой же (и даже менее многословный) эффект, если в holder просто написать holder() = delete;.

В общем, спасибо!

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

Да, все правильно понял, и да, будет тот же эффект при удалении конструктора без аргументов

maxis11
()
using xy_type = unsigned long long;
template <xy_type x>
struct fact {
  static constexpr xy_type y = x * fact<x-1>().y;
};

template <>
struct fact<0> {
  static constexpr xy_type y = 1;
};

template<int x>
class Boo
{
public:
    Boo() {}
    ~Boo(){}
    ~Boo(){}
};



template<>
class Boo<fact<9>::y>
{
public:
    Boo() {}
    static_assert(false, "This is the answer");
    ~Boo(){}
    ~Boo(){}
};

Выхлоп компилятора: ../AG/main.cpp:282:5: error: static assertion failed: This is the answer static_assert(false, «This is the answer»); ^ ../AG/main.cpp:284:5: error: ‘Boo<362880>::~Boo()’ cannot be overloaded

next_time ★★★★★
()
Последнее исправление: next_time (всего исправлений: 1)
Ответ на: комментарий от next_time

Довольно таки элегантно, хотя clang при таком решении тоже ответа не выдал, только gcc.

evilface ★★
() автор топика

Раз уж олимпиада пошла:

using xy_type = unsigned long long;
template <xy_type x>
struct fact {
  static constexpr xy_type y = x * fact<x-1>().y;
};

template <>
struct fact<0> {
  static constexpr xy_type y = 1;
};

template <int i>
bool deleted() = delete;

static bool n = deleted<fact<3>::y>();
compile-time-print.cpp:15:37: error: use of deleted function ‘bool deleted() [with int i = 6]’
 static bool n = deleted<fact<3>::y>();
                                     ^
compile-time-print.cpp:13:6: note: declared here
 bool deleted() = delete;
      ^
Забавно, что в данном случае шланг осилиливает, хотя существенных отличий от других вариантов здесь нет.

xaizek ★★★★★
()

как-то frustum на гитхабе выкладывал свое изобретение про испольование си++ в качестве скриптового языка. Сейчас почему-то выпилил репозиторий((((

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

2. Макросы не рекурсивны. __FILE__ уже определен как макрос, а значит не может использоваться как часть макроса (точнее препроцессор не будет повторно искать чему равен __FILE__ после того как он один раз подставил его в твой код)

Какой бред. Нет тут ни рекурсии, ни повторного подстановки. Потому что тут никакой подстановки нет. Всё что после #error — просто строка.

Neither ‘#error’ nor ‘#warning’ macro-expands its argument. Internal whitespace sequences are each replaced with a single space. The line must consist of complete tokens. It is wisest to make the argument of these directives be a single string constant; this avoids problems with apostrophes and the like.

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

А если шаблонный параметр назвать «answer», то оба компилятора выдают [with answer = N], только gcc ещё и добавляет тип. Выглядит красиво.

Без static constexpr оно работает как и с ним, так что оригинальный код можно не править.

А если сочетать это с ответом от KennyMinigun, то можно решить сразу обе проблемы, привязавшись к clang.

2 KennyMinigun: спасибо за ссылку, попробовал, в clang работает, в gcc пока что нет. Но уже очень хорошо, учитывая верхнюю часть данного комментария.

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

как-то frustum на гитхабе выкладывал свое изобретение про испольование си++ в качестве скриптового языка. Сейчас почему-то выпилил репозиторий((((

Печально, было бы любопытно посмотреть.

Там была именно тема в том же духе, «пусть компилятор шаблоны выполняет», или попытка запилить интерпретатор?

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

Там была именно тема в том же духе, «пусть компилятор шаблоны выполняет», или попытка запилить интерпретатор?

там было что-то в стиле «запускаем gcc на лету, линкуем хитрым способом». Что-то типа loadlibrary без loadlibrary. Я даже уже не помню, но решение какое-то интересное, если вы понимаете, кто такой @frustum.

EvilSpirit
()
Ответ на: комментарий от evilface
#define STR(X) #X
#define DEFER(M,...) M(__VA_ARGS__)
#define CUSTOM_ERROR_(a) _Pragma(STR(a))
#define CUSTOM_ERROR(...) CUSTOM_ERROR_(GCC error DEFER(STR, __VA_ARGS__))

#ifndef X
CUSTOM_ERROR(usage: c++ --std=c++11 -fmax-errors=1 -DX=<argument> __FILE__ && ./a.out)
#endif // X
compile-time-print.cpp:7:11: error: usage: c++ --std=c++11 -DX=<argument> «compile-time-print.cpp» && ./a.out
 CUSTOM_ERROR(usage: c++ --std=c++11 -fmax-errors=1 -DX=<argument> __FILE__ && ./a.out)
           ^

Я ещё -fmax-errors=1 добавил, так короче вывод.

xaizek ★★★★★
()

Всё хорошо, но не достигнута одна цель: я хочу выводить fact<X>().y во время компиляции, желательно роняя её

Ты хочешь compile time function execution с выводом результата в консоль? Тогда где у тебя constexpr?

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

Чтобы получить производительность с++ на любой платформе.

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

Был, выкинул, ибо то что я хотел не работало. Теперь понимаю, как сделать чтобы работало, теперь он есть. Собственно, результаты выше по треду.

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

Да, круто, так работает.

Итоговый код, работающий везде:

#include <iostream>
#include <type_traits>

#define STR(X) #X
#define DEFER(M,...) M(__VA_ARGS__)
#define CUSTOM_ERROR_(a) _Pragma(STR(a))
#define CUSTOM_ERROR(...) CUSTOM_ERROR_(GCC error DEFER(STR, __VA_ARGS__))

#ifndef X
CUSTOM_ERROR(usage: c++ --std=c++11 -fmax-errors=1 -DX=<argument> __FILE__)
#endif // X

using xy_type = std::enable_if<std::is_integral<decltype(X)>::value,
      decltype(X)>::type;

template <xy_type x>
struct fact {
  xy_type y = x * fact<x-1>().y;
};

template <>
struct fact<0> {
  xy_type y = 1;
};

constexpr xy_type y = fact<X>().y;

template <xy_type answer>
bool deleted() = delete;

static bool n = deleted<y>();

int main() {
  return 0;
}


Спасибо всем принявшим участие!

(Если у кого-нибудь ещё какие интересные мысли есть — можно делиться, будет любопытно).

evilface ★★
() автор топика

Ввод организовать легко, через -D, а с выводом придумать не могу. Есть идеи?

#warn

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

Стоит читать внимательнее. Выводить нужно было результат вычислений, произведённых уже после подстановки макросов.

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