Soru Yeni infix operatörlerini tanımlama


C ++ 11'e teşekkürler, şimdi "syntactic sugar" a ulaşabileceğim en yakınları oluşturmak için makroları, kullanıcı tanımlı edebi parçaları, lambayı, vs. birleştirmek mümkün. Bir örnek olurdu

 if (A contains B)

Tabii ki bu kolay.

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

İfade, sol taraftaki ve sağ taraftaki argümanları içeren özel bir yapı olan bir boole dönüştürür. Tabii ki, aşırı yüklenen operatör dizisi, ilk önce özel dizgiyi, daha sonra kendisini geri döndürerek, yapının kendisi için operatör + yükler.

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+

Şimdi daha ileri gitmek istediğime karar verdim. Ne dersin

(x in a) perform cube

Liste kavraması yok, ama bu iyi bir örnek değil mi? İlk başta dedim ki, özel operatör önceliğini sormak için stackoverflow'a gitmem gerekiyordu, fakat doğru akılda hiç kimse kodumu kullanamayacağı için parantez içinde parantez içine almak doğru. Bunun yerine, diğer örneğime genişledim ve 'içerdi' gibi özel yapılar olarak 'içeride' ve 'gerçekleştirme' yaptım.

Daha ileri gidebilir ve şablonun herhangi bir sayısal indeks ve herhangi bir kap olarak olabilmesi için şablon atabilirsiniz, ancak basitlik için x'i bir tamsayı olarak ve bir vektörün bir vektörü olarak bıraktım. Şimdiye kadar, aslında, yerel değişken x'i bir argüman olarak almaz, onu yerel olarak operatör string () işlevinde kullanır.

Bazı şeyleri basitleştirmek için, ifadenin sonuçlarını bir dizede saklıyorum.

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

Başka bir soruya şükür: Tip kesinti için aşırı yükleme görevlendirme operatörü

Bir ödev olarak geri dönmek için pratik bir kullanım olduğunu anladım:

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;

Sayesinde milleniumbug'un cevabı, Yapabilirim:

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

İki uyarı var. Öncelikle, bir int geri döndüğümde onu çalıştırmak için bir kukla değişkene atayabilirim. Daha önce yaptığım result_struct şeyini yapabilirim ya da std :: function nesnesini kendi başına çağırırdım, ama kendimi tekrar ediyorum. Diğer uyarı ise, makroda çok fazla sayıda var olduğu için, lhs'yi değiştiremezsiniz (ki bu bir yineleyici belirlemenize izin vermez).

Her şey düşünüldüğünde, aşağıdaki gibi beklendiği gibi çalışır.

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;

Yeni sürüm

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;

Bunu, infix operatörleri yerine, postfix operatörleri gibi yapmalıyım. "String literal"s.contains "Other string literal"sveya işlev stili yapar mı "String literal"s.contains("Other string literal"s)?

Kodumu daha genişletilebilir hale getirmek için nasıl geliştirebilirim? Şu anda olduğu gibi, çok kirli. Bunu yapmak için daha iyi / daha genel / daha az karmaşık bir yol var mı? Örneğin, ifadeleri tanımlamak veya kodları yeniden kullanmamak için ifadeleri genelleştirmek.


24
2018-03-26 08:10


Menşei


Bu, gizleme için iyi bir tekniktir, ancak aksi halde kesinlikle önlenmelidir. Kodunuzu okumak için C ++ 'yı bilen biri için bunu imkansız kılar. - James Kanze
İfade şablonlarını yeniden icat ediyor gibisiniz ... terime bakın ve bol miktarda bulmalısınız. Ancak yeni infix operatörlerini dile tanıtmak için kötü bir fikir. Fonksiyon çağrı notasyonu ile sopa veya popülerlik kaybetmek. - Potatoswatter
Ben bunu çekiyorum çünkü ilginç ve temiz, ama kesinlikle gerçek kodda gerçek kullanım için onu savunmam. - Jamin Grey
Bu korkunç hack. Bir betik dili istiyorsanız, birini kullanın. - Walter
Buna değer, C ++ 11'den önce bu mümkün olmuştur. Görmek: stackoverflow.com/questions/1515399/... - Cogwheel


Cevaplar:


En son düzenlemenin tüm soruları içerdiğini varsayarak, burada sorulan sorunun ne olduğunu görmek zor.

Bunu infix operatörleri yerine postfix var ki yapmalı mıyım?   "String literal" s.contains gibi işleçler "Diğer dize değişmezi" ler veya   işlev stili yapmak, "String literal" s.contains ("Diğer dize   literal "ler)?

Evet. "String literal"s.contains("Other string literal"s) en iyi yoldur - özlü, C ++ programcılarına açık, diğer dillerin programcılarına açık (Java ve Python dizeleri yöntemleri vardır) ve hiçbir şablon büyüsü veya makro büyüsü kullanılmaz.

Kodumu daha genişletilebilir hale getirmek için nasıl geliştirebilirim? Doğru olduğu gibi   şimdi, çok kirli. Daha iyi / daha genelleştirilmiş / daha az var mı   Bunu yapmanın gizli yolu? Örneğin, ifadeleri genelleştirmek için   Açıklamaları tanımlamaya veya kodu yeniden kullanmamama gerek yok.

Evet! Ama sadece belli bir dereceye kadar (orada ve burada gereksiz şeyleri ortadan kaldırarak):

#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

Ve sonra infix operatörünüzü şöyle oluşturabilirsiniz:

#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";
}

Başka bir makro yönergesinde makro yönerge oluşturmanın bir yolu olmadığı için, #define yönergesini kullanmaktan kaçınmanın bir yolu olmadığını unutmayın.

Varsa bunun pratik faydaları nelerdir?   Bunu gerçek dünya kodu olarak kullanmanın rasyonalitesi. Ne yapabilirsin   bunun için kullanıyorum, rekreasyon amaçlarını engellemek için?)   Arkadaşımın, C ++ öğrenmek yerine kolay bir soyutlamayı istediğini söyle   Bash veya Perl deneyimi için arayüz ama   dış gcc'yi derlemek / bağlantıya başvurmadan işbirliği yapmak. o   şekilde, C ++ 'komutları' veya 'kod' yazabilir ve derleyebilir ve   Programlarım / kütüphanelerim / arayüzüm ile bağlantılandır.

Başka bir dilin üstünde bir dil oluşturmaya çalışıyorsunuz gibi görünüyor. İçin hazırlık

  • Dilinizi test etmeye çalışan saat ve saatler.
  • Utanç verici kötü teşhis mesajları. Bunu derlemeye çalışın: std::vector<void> myarr;1 Sonra makrolarla sarın. Ve sonra başka bir şablona sarın. Ve sonra başka bir makroda ... Bu fikri al.
  • İşlenen kodu gösteren hata ayıklama araçları.
  • Diliniz kendisiyle mükemmel bir şekilde bütünleşse bile, tonlarca kural ve karmaşık tip sistemle ilgilenmek için hala C ++ 'ya sahipsiniz. Sonuçta, tüm soyutlamalar sızdı.

Eğer arkadaşın Perl'de program yapmak istiyorsa, bırak onu yapsın. Bu dillerin C ile arayüz oluşturması kolaydır

Bir dil oluşturmaya çalışıyorsanız, diğer diller yapmaya çalıştığınız şeyi açıkça ifade edemediğinden, ayrıştırıcı üreteçleri (Flex / Bison, ANTLR) ve LLVM bunu kolaylaştırır.

Ayrıştırıcı oluşturuyorsanız, D dil karışımlarına bakın. Derleme zamanında oluşturulmuş bir dizeyi kabul eder ve ardından doğrudan yerleştirilmiş gibi derler.

İşte...

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

eşdeğerdir:

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

Bu sadece basit bir örnektir. Bir dizeyi ayrıştıran işleviniz olabilir:

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

1 - Nasıl göründüğü (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

12
2018-03-29 12:57