cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
89 stars 0 forks source link

Расширить возможности шаблонов до параметризированных синтаксических деревьев #372

Open apolukhin opened 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +8, -3 Автор идеи: NeoProgramming

Шаблон - это объект времени компиляции; при передаче в шаблон какого-то параметра (типа или целочисленной константы), на самом деле передается не тип и не константа, а фрагмент синтаксического дерева.

Поэтому я предлагаю расширить возможности шаблонов, приблизив их к возможностям синтаксических макросов:

  1. разрешить передавать в качестве аргументов любые корректные фрагменты синтаксического дерева, а не только типы и целочисленые константы. В частности, параметрами шаблонов могут быть константные строки; числа с плавающей точкой; корректные идентификаторы (не значения переменных, а именно имена!); выражения; и самый общий случай - корректные блоки кода.

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

Рассмотрим предложение подробнее.

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

template<typename T, const char *S>
struct NamedField
{
     T value;
     const char *name = S;
};
// использование
struct Foo
{
  NamedField<std::string, "User name"> username_;
  NamedField<int, "User ID"> id_;
}

Строковые литералы и числовые литералы с плавающей точкой ничем не хуже целочисленных; и нет никакой причины запрещать их передачу в шаблоны.

Второе расширение - возможность передачи в шаблоны произвольных корректных фрагментов синтаксического дерева. Фактически, это более общий случай. Передавая в шаблон число или тип, мы передаем на самом деле именно фрагмент синтаксического дерева.

Фрагменты синтаксического дерева просто подставляются в код, в точности как это происходит в лексических макросах препроцессора. Для обозначения фрагментов синтаксических деревьев предлагается использовать отдельное ключевое слово. Можно воспользоваться существующим словом inline. Например, можно передать выражения:

template<inline Condition, inline Expr>
void Foo()
{
   while(Condition)
     Expr;
}
// использование
Foo< (x<0), { x++; } >();

Возможно, имеет смысл предусмотреть несколько ключевых слов для того, чтобы можно было ограничить вид фрагмента синтаксического дерева (т.е. концепты для этого). Отдельными видами могут быть

Интересное следствие - возможность передавать в шаблоны типы, сформированные "на лету":

std::vector< struct { int x, y, z; } > myVec;

Это может быть полезно для различной кодогенерации.

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

template<unsigned int N, inline Expr>
inline FixedLoop
{
   for(unsigned int i=0; i<N; i++)
    Expr;
}

// применение
//...
FixedLoop<10, { foo(); }>;
//...

Как частный случай, именованные блоки кода могут быть и не шаблонными. Т.е. шаблонные блоки кода это аналог параметризированных макросов (#define) то не шаблонные - аналог макросов без параметров. Просто заготовки кода, которые непосредственно вставляются туда, где вставлены их имена.

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

apolukhin commented 3 years ago

Андрей Руссков, 7 ноября 2018, 20:37 емнип в с++20 можно будет параметризовать шаблоны constexpr значениями constexpr-сравниваемых типов. Под эту категорию попадают и указатели на функции. Например, clang 6 в режиме c++2a уже отлично кушает вот такой вот код:

#include <iostream>

template <auto func>
void foo() {
    func();
}

int main() {
    foo<+[] { std::cout << "Hello, world!" << std::endl; }>();
}

Правда, не сумеет с полиморфными лямбдами. Вкупе с template template параметрами из c++17 возможно, всё что вам нужно, уже есть?

languagelawyer, 1 февраля 2019, 4:49 clang 9.0.0 error: a lambda expression cannot appear in this context

Анатолий Томилов, 26 декабря 2018, 14:08 Я задавал подобный вопрос на SO: основная сложность, на сколько я помню, -- это манглинг имён (его сложно будет реализовать; намного сложнее, чем есть сейчас).

languagelawyer, 3 февраля 2019, 14:02

при передаче в шаблон какого-то параметра (типа или целочисленной константы), на самом деле передается не тип и не константа, а фрагмент синтаксического дерева.

Да это про что угодно можно сказать. При передаче аргумента в функцию передаётся фрагмент синтаксического дерева, при инициализации переменной и т.д.