LINUX.ORG.RU

Генерирование lookup table в compile-time

 , ,


1

3

Хочется заполнить constexpr std::array на стадии компиляции, но clang 4.0 с флагом -std=c++1z ругается:

non-constexpr function 'operator[]' cannot be used in a constant expression
                lookupTable[i] = f(i);

Какой тогда толк от constexpr std::array, если его нельзя по нормальному использовать?

Начал пробовать по разному и получилось следующим образом:


template <size_t N>
struct CompileTimeLookupTable {

    using TableValue = double;
    using LookupTable = std::array<TableValue, N>;

    constexpr TableValue f(size_t);

    constexpr static auto createLookupTable() {
        LookupTable lookupTable {};
        ...
        const_cast<TableValue &>(static_cast<const LookupTable &>(lookupTable)[i]) = f(i); // <--- смущает этот момент
        ...
        return lookupTable;
    }

    constexpr static auto _lookupTable { createLookupTable() };

};

Это UB или не UB? Как сделать менее костыльно?



Последнее исправление: Nietzsche (всего исправлений: 1)

Какая буква в слове const тебе непонятна? Или вызывает затруднение то что оператор [] _меняет_ значение? Касты естественно UB. Тебе нужно сразу создать массив заполненный нужными значениями.

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

Какая буква в слове const тебе непонятна? Или вызывает затруднение то что оператор [] _меняет_ значение?

Мне непонятна причина, по которой operator[] не помечен как constexpr.

Тут он constexpr ---> http://en.cppreference.com/w/cpp/container/array/operator_at

constexpr reference operator[]( size_type pos );
		(since C++17)

Но почему-то шланг4.0 об этом не в курсе, хотя я ему указал -std=c++1z

Тебе нужно сразу создать массив заполненный нужными значениями.

Зачем тогда нужен constexpr? Удобно ведь. К тому же мой код работает, хотя и пахнет от него грязным хаком. Вот мне и интересно, насколько он UB.

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

Это же stl, а у clang'а его нет и он ворует stl у gcc который clang по-хитрому алгоритму ищет в твоей системе.

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

Мне непонятна причина, по которой operator[] не помечен как constexpr.

Добавил constexpr к lookupTable внтури функции createLookupTable().

constexpr static auto createLookupTable() {
    constexpr LookupTable lookupTable {};
    ...

Теперь, судя по всему, operator[] помечен как constexpr, но это не упрощает задачу.

constexpr variable '_lookupTable' must be initialized by a constant expression
        constexpr static auto _lookupTable { createLookupTable() };
modification of object of const-qualified type 'const CompileTimeLookupTable<666>::TableValue' is not allowed in a constant expression
                const_cast<TableValue &>(static_cast<const LookupTable &>(lookupTable)[i]) = f(i);

Как не крутился - работает только код из первого сообщения.

Nietzsche
() автор топика

Возможно, причина в том, что i вовсе не constexpt

anonymous
()

gcc 5.4.0 такое скомпилил:

    constexpr static auto createLookupTable() {
        constexpr LookupTable lookupTable {};
        const_cast<TableValue &>(static_cast<const LookupTable &>(lookupTable)[13]) = f(13);
        return lookupTable;
    }

clang 4.0 продолжает ругаться:

modification of object of const-qualified type 'const CompileTimeLookupTable<666>::TableValue' is not allowed in a constant expression
                const_cast<TableValue &>(static_cast<const LookupTable &>(lookupTable)[13]) = f(13);
Nietzsche
() автор топика
Ответ на: комментарий от Nietzsche

Без constexpr LookupTable lookupTable {}; компилится и на gcc и на clang:

    constexpr static auto createLookupTable() {
        LookupTable lookupTable {};
        const_cast<TableValue &>(static_cast<const LookupTable &>(lookupTable)[13]) = f(13);
        return lookupTable;
    }

Это вообще можно юзать, или оно отвалится при следующем обновлении компилятора?

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

Я считаю что лучше вообще применять кодогенерацию (т.е. если кодогенератор писать на самих же плюсах, то можно через fprintf или ofstream)

SZT ★★★★★
()

Чем не устраивают решение из гугла по запросу «Create N-element constexpr array in C++11»? И не нужен C++17.

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

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

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

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

Я тоже считаю, что Тьюринг полнота языка шаблонов C++ на практике оказывается бесполезной :-) Поэтому рядом с макросами Лиспа шаблоны C++ не стоят :-)

anonymous
()

Остановился на таком варианте:

#include <iostream>
#include <array>

template <typename T, auto N, T(*F)(const T &)>
struct LookupTable {

	constexpr static auto create() {
		std::array<T, N> table {};
		const auto & tableConstRef = table;
		for(auto i = 0; i < N; ++i) {
			const_cast<T &>(tableConstRef[i]) = F(i);
		}
		return table;
	}

	constexpr static const auto table { create() };
};


int main() {

	std::cout << LookupTable<int, 123, [](auto n) { return n * n; }>::table[30] << "\n";  // 900

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