Quindi grazie a C++ 11, è ora possibile combinare macro, letterali definiti dall'utente, lambda, ecc. Per creare il più vicino possibile allo "zucchero sintattico". Un esempio potrebbe essereDefinizione di nuovi operatori di infisso
if (A contains B)
Naturalmente questo è facile.
cout <<("hello"_s contains "ello"_s)<<endl;
L'espressione converte in un bool, dove contiene è una struttura personalizzato che prende il lato sinistro e il lato destro come argomenti. La struct ovviamente sovraccarica l'operatore + a prendere prima la stringa personalizzata letterale, restituendo se stessa, quindi l'operatore + per la struttura stessa.
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+
Ora ho deciso che voglio andare oltre. Che dire di
(x in a) perform cube
Non è una lista di comprensione, ma è un buon esempio, giusto? All'inizio ho detto, beh, dovrei andare su StackOverflow per chiedere la precedenza degli operatori personalizzati, ma è semplice metterlo tra parentesi dato che nessuno sano di mente userebbe il mio codice. Invece, ho ampliato il mio altro esempio e ho "in" e "perform" come strutture personalizzate, proprio come "contiene".
Puoi andare oltre e stamparlo in modo che x possa essere un qualsiasi indice numerico e un qualsiasi contenitore, ma per semplicità ho lasciato x come numero intero e come vettore di valori. Ora, finora, non prende la variabile locale x come argomento, la utilizza localmente nella funzione string() dell'operatore.
Per semplificare le cose, ho memorizzare i risultati dell'espressione in una stringa, in questo modo
operator string() const {
string s = "";
for (int x : lhs.rhs)
s += to_string(rhs(x)) + string("\n");
return s;
}
Grazie ad un'altra domanda: Overloading assignment operator for type deduction
ho capito un'utilità pratica per la restituzione come un incarico è il seguente:
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;
Grazie a milleniumbug's answer, posso fare:
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() +
Ci sono due avvertimenti. Innanzitutto, restituisco un int in modo che possa assegnarlo a una variabile dummy per farlo eseguire. Potrei sempre fare la cosa result_struct che ho fatto prima, o restituire un oggetto std :: function per chiamarlo da solo, ma mi ripeterò. L'altra avvertenza è che poiché ci sono così tante conte nella macro, non è possibile modificare lhs (che non consente di specificare un iteratore).
Tutto considerato, il seguente funziona come previsto.
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;
Nuova versione
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;
Devo fare in modo che invece di operatori infissi, ci sono operatori in forma suffissa, come "String literal"s.contains "Other string literal"s
, o farlo funzione di stile, "String literal"s.contains("Other string literal"s)
?
Come potrei migliorare il mio codice per renderlo più estensibile? Come è adesso, è molto inquinato. C'è un modo migliore/più generalizzato/meno goffo per farlo? Ad esempio, per generalizzare le espressioni in modo che non sia necessario definire istruzioni o riutilizzare il codice.
Questa è una buona tecnica per l'offuscamento, ma dovrebbe sicuramente essere evitato altrimenti Rende impossibile per qualcuno che conosce il C++ leggere il tuo codice. –
Sembra che tu stia reinventando i modelli di espressione ... cerca il termine e dovresti trovare molto. Ma per quanto riguarda l'introduzione di nuovi operatori infissi nel linguaggio, cattiva idea. Attacca con la notazione di chiamata di funzione o perde popolarità. – Potatoswatter
Sto inviando questo messaggio perché è interessante e accurato, ma sicuramente non lo sostengo per l'uso effettivo nel codice reale. –