2013-03-26 3 views
23

Итак, благодаря C++ 11, теперь можно комбинировать макросы, пользовательские литералы, lambdas и т. Д., Чтобы создать ближайший я могу получить «синтаксический сахар». Примером может служитьОпределение новых инфиксных операторов

if (A contains B) 

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

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

Выражение преобразуется в логическое значение, где содержится обычай структура, которая принимает левую и правую часть в качестве аргументов. Структура курсора перегружает 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, чтобы спросить о пользовательском приоритете оператора, но прямо поставить его в круглые скобки, поскольку никто в здравом уме не будет использовать мой код. Вместо этого я расширил мой другой пример и «включил» и «выполнил» как пользовательские структуры, как «contains».

Вы можете пойти дальше и сделать шаблон таким, чтобы 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; 
} 

Благодаря другой вопрос: Overloading assignment operator for type deduction

я понял одно практическое применение для возвращения его в качестве назначение является следующим:

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's 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; 

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

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

+14

Это хороший метод для запутывания, но должны определенно следует избегать иначе. Это делает невозможным для кого-то, кто знает C++, читать ваш код. –

+0

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

+4

Я поддерживаю это, потому что это интересно и аккуратно, но я определенно не защищаю его за фактическое использование в реальном коде. –

ответ

11

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

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

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

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

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

#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 содержит директиву, так как нет способа создать директиву макроса с другой директивой макросов.

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

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

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

Если ваш друг хочет запрограммировать программу на 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")); 

- Вот как это выглядит (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 

 Смежные вопросы

  • Нет связанных вопросов^_^