LINUX.ORG.RU

Метапрограммирование - проблемы и пути их решения.

 , , ,


12

6

Пичал я тут на днях токенайзел для C++-кода, но всё это меня добило я решил поделится.

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

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

Чтобы не быть голословным пишем что-то типа

constexpr uint64_t f(uint64_t a, uint64_t b) {
  return a + b; 
}
Всё ок, но пишем что-то сложнее, аля:

uint64_t m[] = {0, 1, 2, 3, 4};
constexpr uint64_t f(uint64_t a, uint64_t b) {
  return m[a] + m[b]; 
}

Бида( или это моё неосиляторство плюсов?), дак зачем они запилили эту фичу, если она может лишь галимую примитивщину? Шаблоны ещё ущербней. В чем приемущество? Зачем?

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

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

У меня есть 3 пути: терпеть, пилить свой язык и конпелятор самому( что долго и нудно) и ваш совет.

Эка ты привередливый, языков over9000100500 уверен ты найдёшь свою прелесть.

Dron ★★★★★ ()
const uint64_t m[] = ...
anonymous ()

да можно на сишке сделать через жопу(енумы) и макросы

C чего ты взял что это через *опу?, да и вообще смотря на твой код я так и не понял что тут особенного? Написал функцию, передал параметры, получил значения, что не так?

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

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

И да, суть не в этом.

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

Макросы слишком приметивны и черезжопны, против енумов ничего не имею.

Массивы, сложность в массивах. Мне пришлось компипастить очень много, ибо я не мог сделать m[c] - проблема раз. И да, компилтайм.

superhackkiller1997 ()

когда коту нечего делать, он лижет яйца, бери пример

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

Я тебе открою страшную тайну, но из всех 100500 ЯП, которые я знаю, вменяемых всего 2-3. Поэтому авось есть какой-то язык, о котором никто не знает.

Хотя я понимаю, что скорее всего тут будет срач и на 10-м сообщени тема загнётся, но авось.

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

Перечисли для начала все 100500 языков и те самые 2-3 выдели.

anonymous ()

Плохой, плохой язык C++! Как он мог сделать так, что ты его не осилил. Ему стыдно. И г-ну Страуструпу тоже.

schizoid ★★★ ()

метапрограммирование

c++

Помогите, порвал шаблон.

buddhist ★★★★★ ()

почитал сообщения ТС - рекомендую ему не заморачиваться и сразу брать Common Lisp, они созданы друг для друга

wota ★★ ()

С++

Проблема очевидна.

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

Это же тутошний известный эксперт по всем ЯП. Всем делать ку 2 раза

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

почитал сообщения ТС - рекомендую ему не заморачиваться и сразу брать Common Lisp

Я думаю, ему лучше подойдет J или что-то сравнимо наркоманское.

tailgunner ★★★★★ ()

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

nerdogeek ()

Продублирую свой удаленный пост (теперь цензурно):

Так конечно не работает:

#include <stdint.h>

uint64_t m[] = {0, 1, 2, 3, 4};

constexpr uint64_t f(uint64_t a, uint64_t b) {
  return m[a] + m[b]; 
}


int main()
{
    int a[f(1,2)];
}

Потому что ты ж нифига не понял, как обращаться с constexpr.

А так - работает:

#include <stdint.h>

constexpr uint64_t m[] = {0, 1, 2, 3, 4};

constexpr uint64_t f(uint64_t a, uint64_t b) {
  return m[a] + m[b]; 
}


int main()
{
    int a[f(1,2)];
}
Pavval ★★★★★ ()

пилить свой язык и конпелятор самому( что долго и нудно)

lol

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

Просто уровень экспертизы примерно такой же.

anonymous ()

У меня есть 3 пути: терпеть, пилить свой язык и конпелятор самому( что долго и нудно) и ваш совет.

Есть еще 4й путь, самый Ъ - перед тем как устраивать плач Ярославны про гадкий С++ и свои мучения с оным, хоть одним глазом заглядывать в документацию. В вики русским по белому написано (первая ссылка вот отсюда http://yandex.by/yandsearch?text=C++ constexpr)

Использование constexpr порождает очень жёсткие ограничения на действия функции:

   1. такая функция не может быть типа void;
   2. тело функции должно быть вида return выражение;
   3. выражение должно также быть константой, а также может вызывать только те функции, что также обозначены ключевым словом constexpr, или просто использовать обычные константы;
   4. функция, обозначенная constexpr, не может вызываться до того момента, пока она не определена в текущей единице компиляции.

Медитируйте над п.3. вплоть до просветления.

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

тут будет срач и на 10-м сообщени тема загнётся

Наоборот же, на 10-м сообщении набигут ёбнутые лиспофанбои и будет лиспосрач. Это же ЛОР.

anonymous ()

я в очередной раз убедился в ущербности плюсов и так и не понял логики тех, кто это запилил

Дай поправлю: «я так и не понял тех, кто это запилил, и ещё раз убедился в ущербности своей логики». Не благодари.

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

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

Т.е., теперь я не могу менять значения в m[]? Отлично, очередная защита от дурака, которая пичалит меня.

А можно заполнить массивчик в компилтайме? Я хочу, чтобы m[] был заполнен степенями двойки.

Всё это как-то слабо, вяло и не интересно.

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

А ну да, ну да.

Давай разберём мою логику, и логу создателей:

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

Логика создателей: Ты можешь юзать только константы, которые в компилтайме не менняются( почему же?). И мы клали на всё - тебе нужно так делать. Тебе надо пилить 2 функции, чтобы одну юзать в компилтайме, а вторую в рантайме, ибо мы СОЗДАТЕЛИ. Я отвалась из-за наших типанагромаждений в первую очеред и до попытки реализовать твой код даже не дойду.

Да, вторая логика намного лучше.

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

Т.е., теперь я не могу менять значения в m[]? Отлично, очередная защита от дурака, которая пичалит меня.

«Можно придумать защиту от дурака, но только от не изобретательного.»(ц) В данном случае для изобретательных дураков есть type cast...

А можно заполнить массивчик в компилтайме? Я хочу, чтобы m[] был заполнен степенями двойки.

Внезапно, скорость работы 1<<i и m[ i ] практически неразличимы. Или жжение заполнить массивчик степенями двойки в компилтайме проистекает из религиозных побуждений?

Всё это как-то слабо, вяло и не интересно.

Напротив, все это безумно смешно!

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

Логика создателей: Ты можешь юзать только константы, которые в компилтайме не менняются( почему же?). И мы клали на всё - тебе нужно так делать. Тебе надо пилить 2 функции, чтобы одну юзать в компилтайме, а вторую в рантайме, ибо мы СОЗДАТЕЛИ. Я отвалась из-за наших типанагромаждений в первую очеред и до попытки реализовать твой код даже не дойду.

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

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

Он мало жжет. Похож на раннего фрактала, только от программирования

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

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

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

Зато для C в gcc есть грязный хак: атрибуты cont и pure. Наверное, это даст даже более крутые оптимизации, нежели подсчет чего-то в compile-time

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

«Можно придумать защиту от дурака, но только от не изобретательного.»(ц) В данном случае для изобретательных дураков есть type cast...

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

Внезапно, скорость работы 1<<i и m[ i ] практически неразличимы. Или жжение заполнить массивчик степенями двойки в компилтайме проистекает из религиозных побуждений?

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

Напротив, все это безумно смешно!

Ага, очень, прям смеюсь от натуги.

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

А ну да, это те люди, которые пишут реактивный по меркам пхп код? Ну да, мне очень интересных их техники месения кучей на стл.

Зачем определять размер массивов константой? Это последнее, как я буду юзать константы. АХ да, я вспонил, что это мне напомнило:

char V[4];
    char d[4];
    char l[4];
    char p[MAX],i[MAX];
    char w[MAX],ert[MAX];
    char y[MAX],yut[MAX],add1[MAX];
    int D[MAX],l2=0;
    int a=0;
    int dd=0,mm=0,yy=0;
    int hehe=0,hey=1,i2=0;
    int k2=0,t=0,n=0;
    int k=0,rr=0,zed=0;
    int l1=0;
    int wou=0,wwa=0;
Гениально.

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

Почти бесполезны, ибо мне проще прозрачно реализовать самому эти атируты там, где это надо и КАК надо. Так же бесползено шаблонное обобщение плюсов, которое не даёт перфоманса вообще( ну разве против реально недокода), ибо не учитывает особенности елементов, но это уже другая история.

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

Они полезны, но не портабельны

КАК надо

Ах ты упоротый, разрабы gcc знают хуже тебя, да

onanij ()

терпеть, пилить свой язык и конпелятор

Я не понимаю. А чего не написать патч к существующим компиляторам то? Исходники открыты. Что мешает?

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

Отлично, очередная защита от дурака, которая пичалит меня.

Ржал.

А можно заполнить массивчик в компилтайме? Я хочу, чтобы m[] был заполнен степенями двойки.

Явно - нет. Рекурсивную структуру - можно.

#include <iostream>

template <int I, bool B>
struct TestedIndex
{
    enum { value = I };
};

template <int I, int _N>
struct ArrayIndex : public TestedIndex<I, I >= 0 && I < _N>
{
};

template <typename T, int N>
class ValueHolder : private ValueHolder<T, N - 1>
{

public:
    template <typename Callable>
    constexpr ValueHolder(Callable inFunc)
        : ValueHolder<T, N - 1>(inFunc),
        mValue(inFunc(N))
    {
    }


    template <int N1>
    constexpr T at()
    {
        return atImpl(ArrayIndex<N - 1 - N1, N>());
    }

protected:
    template <int N1>
    constexpr T atImpl(TestedIndex<N1, true>)
    {
        return ValueHolder<T, N - 1>::atImpl(TestedIndex<N1 - 1, true>());
    }

    constexpr T atImpl(TestedIndex<0, true>)
    {
        return mValue;
    }

    template <int N1>
    constexpr T atImpl(TestedIndex<N1, false>)
    {
        class Index_Out_Of_Range{};
        return Index_Out_Of_Range();
    }

    const T mValue;
};

template <typename T>
class ValueHolder<T, 0>
{
public:
    template <typename Callable>
    constexpr ValueHolder(Callable) {}
};

using namespace std;

constexpr int init(int i)
{
    return 1 << (i - 1);
}

int main()
{
    constexpr ValueHolder<int, 4> v(init);
    constexpr int i = v.at<3>();
    cout << v.at<0>();
    cout << v.at<1>();
    cout << v.at<2>();
    cout << v.at<3>();
    cout << endl;
}

При желании можно добавить operator[](int), но он будет рекурсивен и не constexpr.

При желании можно хаком получить массив.

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

Вот меня всегда это виселило в людях, который не понимают о чем рассуждают.

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

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

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

Про кеширование, я могу хранить дифф там, где это возможно, а не значения, что конпелятор сделать не может и т.п. А всё почему? Патамучто конпелятор не знает ЧТО ТЕБЕ НУЖНО, а как мы знаем основа оптимизации - узкая специализация.

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

На каком-нибудь перле сгенери / преобразуй нужный тебе набор данных в хидер-формате и сунь зависимость в Makefile. Как маленький, ей богу.

Нафига вам вообще эти встроенные препроцессоры да еще на субсете языка, на котором не получается как есть писать? Онанизм какой-то.

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

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

Мне проще написать 3строчки на Сишке, чтобы сгинерить этот массив и записать его в хедер, а потом заикнлюдить, чем писать такие протянки.

Цель поста - как решить эту проблему? Пока решения нет и написать кодогинератор проще.

Ах да, ещё одна проблема - твоя вялая расстановка скобок даёт портянке 30% её длинны - исправь это и напиши по человечески.

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

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

Но это надо редко, поэтому перебиваться гинератором приходится.

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

Поскольку однажды сгенеренные данные все равно статичны (хотя и потенциально мутабельны) — Makefile твой рантайм. И сразу все стает на места.

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

Ну какбэ ты видел как написан гцц? Я изобью себе всё лицо фейспалами, пока досканально разберусь как он работает, но таких недовольст к сишке у меня много и мне надо будет пилить много, поэтому проще запилить самому, но писать оптимизатор - та ещё бида, и мне лень. Хотя 90% оптимизацией обходятся банальным осиляторством, но всё ровно пичально + даже есть юзать гас будет очень долго и пичально.

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

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

Считать всё во время цомпеляния без веских оснований на то - это удел говна вроде тебя

Вот тебе пример, у меня есть массив их тысячи 8байтных и тысячи 1байтных значений, мне надо инкрементировать их всех.

Я возьму или язык с динамической типизацией, или с нормальным встроеным параметрическим полиморфизмом. Смотря, что надо.

Подумай как сделает это С++-конпелятор из шаблонного кода и как сделаю я, и почему 1-й случай будет работать так же, а в торой намного медленней.

Не знаю. Наверное, ты говнокодер. А чем тебя не устроит результат gcc?

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