Non penso che tu possa farlo comodamente. Stai cercando di implementare la tua lingua su C ++, ma C ++ non ha questo tipo di estensibilità.
La prossima cosa migliore è un'interfaccia fluente. Con un'interfaccia fluente, possiamo codificare una grammatica del nostro design nel sistema di tipi. Non possiamo imporre che venga chiamata solo una funzione speciale per blocco C ++, ma possiamo far sì che venga richiamato solo un metodo speciale nella nostra cascata. Ad esempio:
// Mixin for end() operation
struct can_end {
auto end() && -> void {}
};
// Mixin for normal methods
template<typename next_state>
struct can_normal {
auto normal_1() && -> next_state { return {}; }
auto normal_2() && -> next_state { return {}; }
};
// State after special method: only end and normal methods allowed
struct after_special : can_end, can_normal<after_special> {
after_special() = default;
after_special(after_special const&) = delete;
};
// State before special method: end, normal, and special allowed
struct before_special : can_end, can_normal<before_special> {
before_special() = default;
before_special(before_special const&) = delete;
auto special() && -> after_special { return {}; }
};
// Entry point
auto block() -> before_special { return {}; }
Ora possiamo scrivere un metodo a cascata come:
block()
.normal_1()
.special()
.normal_2()
.end();
Ma sarà impedito di invocare il metodo speciale due volte:
block()
.special()
.special() // type error
.end();
Eliminando il copy ctor, impediamo l'utilizzo di una variabile temporanea per acquisire lo stato pre-speciale:
auto temp = block(); // compilation error: deleted copy ctor
temp.special();
temp.special().end();
Poiché tutti i metodi prendono un riferimento di rvalue, questo scoraggia anche gli inganni come legare lo stato a un riferimento e quindi invocare il metodo speciale due volte. Ma sono sicuro che il sistema di tipo C ++ ha abbastanza portelli di escape per farlo se sei veramente determinato.