Подтвердить что ты не робот

Определение новых инфиксных операторов

Итак, благодаря С++ 11, теперь можно комбинировать макросы, пользовательские литералы, lambdas и т.д., чтобы создать самое близкое, которое я могу получить до "синтаксического сахара". Пример:

 if (A contains B)

Конечно, это легко.

cout <<("hello"_s contains "ello"_s)<<endl;

Выражение преобразуется в bool, где contains - это настраиваемая структура, которая принимает в качестве аргументов левую и правую стороны. Структура курсора перегружает operator +, чтобы сначала взять пользовательский строковый литерал, возвращаясь, а затем оператор + для самой структуры.

struct contains_struct {
    string lhs;
    string rhs;
    void set_lhs(string lhs) { this->lhs = lhs; }
    void set_rhs(string rhs) { this->rhs = rhs; }
    operator bool() const {
        return string::npos != lhs.find(rhs);
    }
} contains_obj;

contains_struct& operator+(const string& lhs, const contains_struct& rhs) {
    contains_obj.set_lhs(lhs);
    return contains_obj;
}

contains_struct& operator+(const contains_struct& lhs, const string& rhs) {
    contains_obj.set_rhs(rhs);
    return contains_obj;
}

#define contains +contains_obj+

Теперь я решил, что хочу пойти дальше. Что насчет

(x in a) perform cube

Это не понимание списка, но это довольно хороший пример? Сначала я сказал, что мне нужно пойти в stackoverflow, чтобы спросить о пользовательском приоритете оператора, но он прямо поставил его в круглые скобки, так как никто в здравом уме не использовал бы мой код. Вместо этого я расширил свой другой пример и "включил" и "выполнил" как пользовательские структуры, как "содержит".

Вы можете пойти дальше и сделать шаблон таким, чтобы x мог быть любым числовым индексом, а как любой контейнер, но для простоты я оставил x как целое число и a как вектор int. Теперь до сих пор он фактически не принимает локальную переменную x в качестве аргумента, она использует ее локально в функции operator().

Чтобы упростить вещи, я сохраняю результаты выражения в строке, например

operator string() const {
    string s = "";
    for (int x : lhs.rhs)
        s += to_string(rhs(x)) + string("\n");
    return s;
}

Благодаря другому вопросу: Перегрузка оператора присваивания для вывода типа

Я понял, что одно практическое использование для его возврата в качестве назначения:

struct result_struct {
    vector<int> results;
    result_struct(vector<int> results) { this->results = results; }
};

...

    operator result_struct() const {
        vector<int> tmp;
        for (int x : lhs.rhs)
            tmp.push_back(rhs(x));
        return result_struct(tmp);
    }

...

result_struct result_2 = (x in a) perform cube;
    for (int x : result_2.results)
        cout <<x<<endl;

Благодаря milleniumbug answer, я могу сделать:

struct for_obj
{
    int _lhs;
    std::vector<int> _rhs;
    for_obj(int lhs, std::vector<int> rhs)
        : _lhs(lhs), _rhs(rhs) { }
};

INFIX_OPERATOR(for_obj, in_op, int, std::vector<int>)
{
    return for_obj(lhs(), rhs());
}
#define in + in_op() +

INFIX_OPERATOR(int, perform_op, for_obj, std::function<int(int)>)
{
    for (int i = 0; i < lhs()._rhs.size(); i++)
        rhs()(lhs()._rhs[i]);
    return 0;
}
#define perform + perform_op() +

Есть два оговорки. Во-первых, я возвращаю int, чтобы я мог назначить его фиктивной переменной, чтобы заставить ее выполнить. Я всегда мог сделать результат result_struct, который я делал раньше, или вернуть объект std:: function, чтобы вызвать его сам по себе, но я бы повторил сам. Другое предостережение состоит в том, что, поскольку в макросе так много consts, вы не можете изменить lhs (что не позволяет вам указать итератор).

Все рассмотренные вещи работают следующим образом.

int x = 0;
std::vector<int> nums = { 1, 2, 3 };
auto cube = [] (int x)
{
    std::cout << x * x * x << std::endl;
    return x * x * x;  
};
int i = (x in nums) perform cube;

Новая версия

class PerformObj {
    int counter;
public:
    PerformObj() : counter(0) { }
    ~PerformObj() { }
    InObj lhs;
    std::function<int(int)> rhs;

    operator int() const {
        return rhs(lhs.rhs[counter]);
    }
} performobj;

#define perform + performobj +

PerformObj& operator+(const InObj& lhs, PerformObj& rhs) {
    rhs.lhs = lhs;
    return rhs;
}

PerformObj& operator+(PerformObj& lhs, const std::function<int(int)>& rhs) {
    lhs.rhs = rhs;
    return lhs;
} 

int main()
{
    std::vector<int> nums = {1,2,3};
    int x = 0;

    auto cube = [] (int n) {
        return n * n * n;
    };

    std::cout << x in nums perform cube << std::endl;
}

explicit operator std::vector<int>() const {
    std::vector<int> temp;
    for (int i = 0; i < lhs.rhs.size(); i++) {
        temp.push_back(rhs(lhs.rhs[i]));
    }
    return temp;
}

int y = 0;
std::cout << y in static_cast<std::vector<int>>(x in nums perform cube) perform std::function<int(int)>([] (int i) -> int {
        return i;
}) << std::endl;

Должен ли я сделать так, чтобы вместо операторов infix существовали постфиксные операторы, такие как "String literal"s.contains "Other string literal"s, или выполняли стиль функции, "String literal"s.contains("Other string literal"s)?

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

4b9b3361

Ответ 1

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

Должен ли я сделать так, чтобы вместо инфиксных операторов появились postfix операторы, такие как "String literal" s.contains "Другие строковые литералы" или сделайте это стиль функции, "String literal" s.contains( "Другая строка буквальный" s)?

Да. "String literal"s.contains("Other string literal"s) - лучший способ - лаконичный, понятный программистам на С++, понятный программистам других языков (у строк Java и Python есть методы), а магия шаблона и магия не используются.

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

Оп! Но только в определенной степени (удаляйте ненужные константы там и здесь):

#define INFIX_OPERATOR(rettype, name, LT, RT) \
struct name\
{\
private:\
    LT* left;\
    RT* right;\
\
protected:\
    LT& lhs() const { return *left; }\
    RT& rhs() const { return *right; }\
\
public: \
    friend name operator+(LT& lhs, name && op)\
    {\
        op.left = &lhs;\
        return op;\
    }\
\
    friend name operator+(name && op, RT& rhs)\
    {\
        op.right = &rhs;\
        return op;\
    }\
\
    name () : left(nullptr), right(nullptr) {}\
\
    operator rettype() const;\
};\
\
inline name :: operator rettype() const

И затем вы можете создать свой инфиксный оператор следующим образом:

#include <iostream>
#include <string>

INFIX_OPERATOR(bool, contains_op, const std::string, const std::string)
{
    return std::string::npos != lhs().find(rhs());
}
#define contains + contains_op() +

int main()
{
    std::string a = "hello";
    std::string b = "hell";
    if(a contains b)
        std::cout << "YES";
}

Обратите внимание, что нет способа избежать #define содержит директиву, так как нет возможности создать директиву макроса с другой директивой макроса.

Каковы практические преимущества этого, если они есть (игнорирование всех рациональность использования этого как реального мира. Я имею в виду, что вы можете получить из-за этого, за то, для чего я его использую, за исключением рекреационных целей?) Скажите, что мой друг, вместо обучения С++, хочет легко абстрагироваться интерфейс для его опыта Bash или Perl, но хотел бы сотрудничать, не прибегая к компиляции/связыванию внешних gcc. Что Кстати, он может писать "скрипты" или "код", который является С++, и компилировать и свяжите его с моими программами/библиотеками/интерфейсом, что угодно.

Кажется, вы пытаетесь создать язык поверх другого языка. Подготовьтесь к

  • Часы и часы, пытающиеся протестировать ваш язык.
  • Смущающие сообщения о плохой диагностике. Попробуйте скомпилировать это: std::vector<void> myarr; 1 Затем заверните его макросами. А затем заверните его в другой шаблон. И затем в других макросах... Вы получаете идею.
  • Инструменты отладки, отображающие обработанный код.
  • Даже если ваш язык отлично интегрируется с самим собой, вы по-прежнему должны иметь С++, с множеством правил и сложной системой типов. В конце концов, все абстракции протекают.

Если ваш друг хочет запрограммировать программу на Perl, просто позвольте ему это сделать. Эти языки легко взаимодействуют с C.

Если вы пытаетесь создать язык, потому что другие языки не могут четко выразить то, что вы пытаетесь сделать, генераторы парсеров (Flex/Bison, ANTLR) и LLVM упрощают работу.

Если создание синтаксического анализатора является излишним, взгляните на смесители языка D. Они принимают строку, созданную во время компиляции, а затем компилируют ее, как если бы она была вставлена ​​напрямую.

Здесь...

import std.stdio;
int main()
{
    mixin(`write("Hello world");`); //`contents` is a raw string literal
    return 0;                       //so is r"contents"
}

эквивалентно:

import std.stdio;
int main()
{
    write("Hello world");
    return 0;
}

Это простой пример. У вас может быть ваша функция, которая анализирует строку:

mixin(user1508519s_language(r"(x in a) perform cube"));

1 - Вот как он выглядит (gcc 4.7.2):

In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_construct.h:63:0,
                 from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:63,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
: In instantiation of 'struct __gnu_cxx::__alloc_traits<std::allocator<void> >':

c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
76:28:   required from 'struct std::_Vector_base<void, std::allocator<void> >'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
208:11:   required from 'class std::vector<void>'
#templateerrors2.cpp:5:19:   required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:189:53: error: no type named 'reference' in 'class std::allocator<void>'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/ext/alloc_traits.h
:190:53: error: no type named 'const_reference' in 'class std::allocator<void>'
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
 In instantiation of 'class std::vector<void>':
#templateerrors2.cpp:5:19:   required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
292:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
467:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: invalid parameter type 'std::vector<void>::value_type {aka void}'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
684:7: error: in declaration 'void std::vector<_Tp, _Alloc>::resize(std::vector<
_Tp, _Alloc>::size_type, std::vector<_Tp, _Alloc>::value_type)'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
881:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:10
8:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1003:7: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
1179:7: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:70:0,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:21
6:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:43
9:5: error: forming reference to void
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/vector.tcc:31
6:5: error: forming reference to void
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:65:0,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
 In instantiation of 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp
= void; _Alloc = std::allocator<void>]':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15:   required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19:   required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9: error: invalid use of 'void'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
 In instantiation of 'void std::_Vector_base<_Tp, _Alloc>::_M_deallocate(std::_V
ector_base<_Tp, _Alloc>::pointer, std::size_t) [with _Tp = void; _Alloc = std::a
llocator<void>; std::_Vector_base<_Tp, _Alloc>::pointer = void*; std::size_t = u
nsigned int]':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
161:9:   required from 'std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _T
p = void; _Alloc = std::allocator<void>]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
247:15:   required from 'std::vector<_Tp, _Alloc>::vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19:   required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
175:4: error: 'struct std::_Vector_base<void, std::allocator<void> >::_Vector_im
pl' has no member named 'deallocate'
In file included from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/bits/stl_algobase.h:66:0,
                 from c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/
c++/vector:61,
                 from #templateerrors2.cpp:1:
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h: In instantiation of 'struct std::iterator_traits<void*>':
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:127:24:   required from 'void std::_Destroy(_ForwardIterator, _ForwardIterato
r) [with _ForwardIterator = void*]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_construct
.h:155:7:   required from 'void std::_Destroy(_ForwardIterator, _ForwardIterator
, std::allocator<_T2>&) [with _ForwardIterator = void*; _Tp = void]'
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_vector.h:
403:9:   required from 'std::vector<_Tp, _Alloc>::~vector() [with _Tp = void; _A
lloc = std::allocator<void>]'
#templateerrors2.cpp:5:19:   required from here
c:\__moje\prog\mingw\bin\../lib/gcc/mingw32/4.7.2/include/c++/bits/stl_iterator_
base_types.h:182:43: error: forming reference to void