tl;dr: Is there a way to add a default argument from the current scope to all implicit constructors in C++?
I am currently designing an interface for an embedded language in C++. The goal is to make the creation of syntactically correct expressions both typesafe and convenient. Right now, I think that learning a heavyweight implementation like boost::proto will itnroduce a too large latency into the development, so I attempt to roll my own implementation.
Here is a small demo:
#include <iostream>
#include <string>
#include <sstream>
class ExprBuilder
{
public:
ExprBuilder(const int val) : val(std::to_string(val)) {}
ExprBuilder(const std::string val) : val(val) {}
ExprBuilder(const char* val) : val(val) {}
ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) {
std::stringstream ss;
ss << "(" << lhs.val << " " << arg.val << ")";
val = ss.str();
}
const ExprBuilder operator()(const ExprBuilder& l) const {
return ExprBuilder(*this, l);
}
template<typename... Args>
const ExprBuilder operator()(const ExprBuilder& arg, Args... args) const
{
return (*this)(arg)(args...) ;
}
std::string val;
};
std::ostream& operator<<(std::ostream& os, const ExprBuilder& e)
{
os << e.val;
return os;
}
int main() {
ExprBuilder f("f");
std::cout << f(23, "foo", "baz") << std::endl;
}
As you can see, it is fairly simple to embedd expressions due to C++ overloading and implicit conversions.
I am facing a practical problem however: In the example above, all data was allocated in the form of std::string objects. In practice, I need something more complex (AST nodes) that are allocated on the heap and managed by a dedicated owner (legacy code, cannot be changed). So I have to pass a unique argument (said owner) and use it for the allocations. I'd rather not use a static field here.
What I am searching is a way to use ask the user to provide such an owner everytime the builder is used, but in a convenient way. Something like a dynamically scoped variable would be great. Is there a way to obtain the following in C++:
class ExprBuilder
{
...
ExprBuilder(const ExprBuilder& lhs, const ExprBuilder& arg) {
return ExprBuilder(owner.allocate(lhs, rhs)); // use the owner implicitly
}
...
};
int main() {
Owner owner; // used in all ExprBuilder instances in the current scope
ExprBuilder f("f");
std::cout << f(23, "foo", "baz") << std::endl;
}
Is this possible?
edit: I'd like to clarify why I do (up until now) not consider a global variable. The owner has to be manually released by the user of the builder at some point, hence I cannot create one ad hoc. Hence, the user might "forget" the owner altogether. To avoid this, I am searching a way to enforce the presence of the owner by the typechecker.