У меня есть два класса, одно выражение (SE
) и набор из двух выражений (ME
).Пакет является самим выражением, следовательно, он может быть элементом другого пакета.
struct SE {
SE(char id, char n) : id(id), n(n) {}
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
char id, n;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r) : lhs(l), rhs(r) { }
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
LHS lhs;
SE rhs;
};
Конструкция пакета выполняет простую проверку достоверности на основе элемента данных n
, доступного в ME
по методу size
.Метод eval
выполняет некоторые вычисления, используя элемент данных id
.Ни n
, ни id
не известны во время компиляции.
Для обоих классов я переопределяю оператор запятой, чтобы он выполнял рекурсивное объединение нескольких отдельных выражений во вложенный пакет.
auto SE::operator,(const SE& r) { return ME<SE>(*this, r); }
auto ME<LHS>::operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
Я хочу, чтобы после того, как весь пакет был построен, метод eval
запускается на всем пакете.Пример:
SE('a',1); // prints 'a'
SE('a',1), SE('b',1); // prints '(a,b)'
SE('a',1), SE('b',1), SE('c',1); // prints '((a,b),c)'
Возможный способ добиться этого - использовать деструкторы классов и добавить флаг is_outer
, который соответствующим образом обновляется при конструировании SE
и ME
.Когда какой-либо из этих классов разрушается, если флаг указывает, что это внешний класс, тогда eval
срабатывает.Полная демонстрация приведена ниже.
Тестирование на godbolt простой функции demo
ниже, как мне кажется, компилятор генерирует больше кода, чем строго необходимо.Хотя id
и n
не известны во время компиляции, последний тип выражения должен быть.Я ожидал бы, что вся конструкция связки сводится к перемещению нескольких чисел в правильное место, а затем проверяет утверждения, но, похоже, на самом деле делает гораздо больше копий.
Можно ли получить большеконструкционной части производится во время компиляции?
#include <iostream>
#include <cassert>
#include <string>
#include <sstream>
using namespace std;
// forward declaration
template <typename LHS> struct ME;
struct SE {
SE(char id, char n) : id(id), n(n), outer(true) {}
SE(const SE& expr) : id(expr.id), n(expr.n), outer(false) {}
ME<SE> operator,(const SE& r);
size_t size() const { return n; }
char *eval(char *b) const { b[0]=id; return b+1; }
~SE() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='\n'; cout << b; } }
char id, n;
mutable bool outer;
};
template <typename LHS>
struct ME {
ME(const LHS& l, const SE& r)
: lhs(l), rhs(r), outer(true) // tentatively set to true
{ l.outer = r.outer = false; assert(l.size() == r.size()); } // reset flag for arguments
ME(const ME<LHS>& expr)
: lhs(expr.lhs), rhs(expr.rhs), outer(false) {}
size_t size() const { return rhs.size(); }
char *eval(char *b) const { *b++='('; b=lhs.eval(b); *b++=','; b=rhs.eval(b); *b++=')'; return b; }
auto operator,(const SE& r) { return ME<ME<LHS>>(*this, r); }
~ME() { if(outer) { char b[20] = {}; char *p=eval(b); *p++='\n'; cout << b; } }
LHS lhs;
SE rhs;
mutable bool outer;
};
ME<SE> SE::operator,(const SE& r) { return ME<SE>(*this, r); }
void demo(char a, char na, char b, char nb, char c, char nc) {
SE(a, na), SE(b,nb), SE(c,nc); // prints '((a,b),c)'
}
int main() {
demo('a',1,'b',1,'c',1);
return 0;
}