Неявный аргумент для конструкторов преобразования

ТЛ; др: Есть ли способ добавить аргумент по умолчанию из текущей области видимости для всех неявных конструкторов в C ++?

В настоящее время я разрабатываю интерфейс для встроенного языка в C ++. Цель состоит в том, чтобы сделать синтаксически правильные выражения максимально безопасными и удобными. Прямо сейчас, я думаю, что изучение тяжеловесной реализации, такой как boost :: proto, приведет к слишком большой задержке в разработке, поэтому я попытаюсь развернуть свою собственную реализацию.

Вот небольшая демонстрация:

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

Как видите, встраивать выражения довольно просто из-за перегрузки C ++ и неявных преобразований.

Однако я сталкиваюсь с практической проблемой: в приведенном выше примере все данные были размещены в виде объектов std :: string. На практике мне нужно нечто более сложное (узлы AST), которые размещаются в куче и управляются выделенным владельцем (устаревший код не может быть изменен). Поэтому я должен передать уникальный аргумент (указанный владелец) и использовать его для распределения. Я бы предпочел не использовать статическое поле здесь.

То, что я ищу, — это способ использования запроса пользователя о предоставлении такого владельца при каждом использовании компоновщика, но удобным способом. Что-то вроде динамически изменяемой переменной было бы здорово. Есть ли способ получить следующее в 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;
}

Это возможно?

редактировать: Я хотел бы уточнить, почему я (до сих пор) не рассматривал глобальную переменную. Владелец должен быть вручную освобожден пользователем разработчика в какой-то момент, поэтому я не могу создать одно специальное. Следовательно, пользователь может вообще «забыть» владельца. Чтобы избежать этого, я ищу способ обеспечить присутствие владельца проверкой типов.

1

Решение

Это вряд ли возможно без глобальных / статических переменных, потому что без глобальной / статической информации локальные переменные Owner owner а также ExprBuilder f не может ничего знать друг о друге.

Я думаю, что самый чистый способ — это добавить

static Owner* current_owner;

к ExprBuilder учебный класс. Затем вы можете добавить новый класс ScopedCurrentOwnerLock, который устанавливает current_owner в конструкторе и устанавливает его в nullptr в деструкторе. Тогда вы можете использовать его как блокировку мьютекса:

class ScopedCurrentOwnerLock {
public:
ScopedCurrentOwnerLock(Owner const& owner) {
ExprBuilder::current_owner = &owner;
}
~ScopedCurrentOwnerLock() {
ExprBuilder::current_owner = nullptr;
}
};

int main() {
Owner owner;
ScopedCurrentOwnerLock lock(owner);
ExprBuilder f("f");
}

Если у вас есть доступ к Owner код, вы можете опустить ScopedCurrentOwnerLock класс и непосредственно установить / снять указатель в конструкторе / деструкторе Owner,

Обратите внимание на следующие две проблемы с этим решением:

  • Если владелец выходит из области действия до того, как блокировка выходит из области действия, у вас есть недопустимый указатель.

  • Статический указатель имеет непредсказуемое поведение, если у вас есть несколько блокировок одновременно, e. г. из-за многопоточности.

1

Другие решения

Все твои ExprBuilders иметь зависимость от Ownerи вы по праву не хотите глобального государства. Таким образом, вы должны передать владельца каждому конструктору.

Если вы действительно не хотите добавлять owner, для всех ваших экземпляров в блоке, вы можете создать фабрику, чтобы передать его для вас.

struct ExprBuilderFactory
{
Owner & owner;
ExprBuilder operator()(int val) { return ExprBuilder(owner, val); }
ExprBuilder operator()(char * val) { return ExprBuilder(owner, val); }
// etc
}

int main() {
Owner owner;
ExprBuilderFactory factory{ owner };
ExprBuilder f = factory("f");
}
0

По вопросам рекламы [email protected]