28

そのため、C ++ 11のおかげで、マクロ、ユーザー定義リテラル、ラムダなどを組み合わせて、「シンタックスシュガー」に最も近いものを作成できるようになりました。例は

 if (A contains B)

もちろん、これは簡単です。

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

式はboolに変換されます。ここで、containsは、左側と右側を引数として取るカスタム構造体です。もちろん、構造体はoperator +をオーバーロードして、最初にカスタム文字列リテラルを取得し、それ自体を返し、次に構造体自体の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」と同じように、「in」と「perform」をカスタム構造体として使用します。

さらに進んで、xが任意の数値インデックスになり、aが任意のコンテナになるようにテンプレート化できますが、簡単にするために、xを整数、aをintのベクトルとして残しました。これまでのところ、実際にはローカル変数xを引数として使用せず、演算子string()関数でローカルに使用しています。

簡単にするために、式の結果を次のように文字列に格納します。

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;

2000年問題の答えのおかげで、私は次のことができます。

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() +

2つの注意点があります。まず、intを返し、ダミー変数に割り当てて実行できるようにします。以前に行ったresult_structをいつでも実行することも、std :: functionオブジェクトを返してそれ自体を呼び出すこともできますが、繰り返します。もう1つの注意点は、マクロには非常に多くの定数があるため、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)ですか?

コードをより拡張可能にするためにどのように改善しますか?今のところ、とても汚染されています。これを行うためのより良い/より一般化された/不格好な方法はありますか?たとえば、式を一般化して、ステートメントを定義したり、コードを再利用したりする必要がないようにします。

4

1 に答える 1

14

最新の編集にすべての質問があると仮定すると、ここで質問が何であるかを理解するのは困難です。

インフィックス演算子の代わりに、「文字列リテラル」s.contains「その他の文字列リテラル」のような後置演算子があるようにするか、関数スタイル「文字列リテラル」s.contains(「その他の文字列リテラル」」にする必要があります。 s)?

はい。"String literal"s.contains("Other string literal"s)は最良の方法です-簡潔で、C ++プログラマーには明確で、他の言語のプログラマーには明確です(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";
}

別のマクロディレクティブを使用してマクロディレクティブを作成する方法がないため、#definecontainsディレクティブを回避する方法がないことに注意してください。

ある場合、これの実際的な利点は何ですか(これを実際のコードとして使用することのすべての合理性を無視します。つまり、レクリエーション目的を除いて、私が使用している目的のためにそれから何を得ることができますか?) 、C ++を学ぶ代わりに、BashまたはPerlの経験のための簡単な抽象化されたインターフェースを望んでいますが、gccの外部でコンパイル/リンクすることに頼らずにコラボレーションしたいと考えています。そうすれば、彼はC ++である「スクリプト」または「コード」を記述し、それをコンパイルして、私のプログラム/ライブラリ/インターフェースなどにリンクすることができます。

別の言語の上に言語を作成しようとしているようです。用意する

  • あなたの言語をテストしようとしている時間と時間。
  • 恥ずかしいほど悪い診断メッセージ。これをコンパイルしてみてください:std::vector<void> myarr;1次に、マクロでラップします。そして、それを別のテンプレートでラップします。そして、別のマクロで...あなたはアイデアを得る。
  • 処理されたコードを表示するデバッグツール。
  • 言語がそれ自体と完全に統合されている場合でも、大量のルールと複雑な型システムを使用して、C++で処理する必要があります。結局のところ、すべての抽象化はリークがあります。

友達が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
于 2013-03-29T12:57:35.927 に答える