Pull to refresh

Простая замена Boost::Optional для использования nullable-типов в проектах C++

Reading time 3 min
Views 7.2K
Статья рассказывает историю о том, как автора напугал монструозный Boost и как он придумал свой маленький nullable-велосипед. Возможно, статья будет полезна тем, кто хочет использовать простые nullable-типы в своём проекте без использования Boost, отвечающие приведённым в статье не самым простым требованиям.

Итак, приступив к поддержке одного относительно большого проекта на MFC, реализующим тонкий биллинг-клиент, я обнаружил, что каждый раз, когда в БД необходимо передать NULL, присутствует дополнительный код, содержащий проверку типа этой (условно):

//...
void CSomeClass::someFunction(double variable)
{
//...
if (variable == 0)
{
    db->addValue(NULL);
}
else
{
    db->addValue(variable);
}
//…

Когда я столкнулся с необходимостью реализации новой фичи в данном проекте, требующей чтения и записи значений NULL, мне тут же захотелось встроить в проект поддержку nullable-типов, которая обеспечит простое и безопасное в плане утечек памяти решение для их создания, присвоения им разных значений, копирования и передачи в те уже готовые функции, которые принимают на вход указатель на значение в памяти.

До сих пор мне не приходилось пользоваться библиотекой Boost, но я слышал о ней и подумал: «Отлично, самое время её пощупать». Тем более, что «гугление» на тему «c++ nullable type» так или иначе жирно намекает на Boost::Optional.

Однако я очень быстро понял, что Boost::Optional мне не подходит (и/или не нравится). Во-первых, её изучение и подключение к проекту не входило в оценку времени работы по задаче, во-вторых, я понял (возможно, тут я ошибся) что получу проблемы при конвертации из Boost::Optional в обычный “указательный” тип C (как минимум, это будет выглядеть не очень красиво в коде), иными словами, я совершенно не был готов к тому, что столкнусь со сложностью для меня в её использовании. Может быть, всё дело в том, что я имел за спиной прочитанную книгу Jeff Alger «C++ for real programmers», в голове имелись различные решения для реализация всяких умных (и не очень) указателей и перегрузки операторов, и поэтому мне было не понятно, почему для реализации nullable-типа, которая мне виделась довольно простой (для моих нужд), нужен такой монстр как Boost::Optional.

А нужды мои следующие (сразу на примере использования моего nullable-велосипедика):

class FooModel 
{
public:
     NullableType<int> intParam;
};
FooModel* model = new FooModel(); // Хочу, чтобы по-умолчанию моя nullable-переменная была равна NULL
bool resBool = model->intParam == nullptr; // Хочу уметь сравнивать значение моей переменной с NULL
model->intParam = 654; // Хочу “просто взять и присвоить” ей значение соотв. типа
resBool = model->intParam == nullptr; // Хочу иметь возможность “просто взять и приравнять” её NULL, не думая об утечках памяти
NullableType<int> globIntParam1 = model->intParam; // Хочу простого копирования значений, не думая об утечках и выделении памяти
int* intVarPtr1 = new int(23); // Хочу полной совместимости с нативными C-указателями
int* intVarPtr2 = NULL;
model->intParam = intVarPtr1; // Хочу чтобы и так можно было
model->intParam = intVarPtr2; // и так тоже. (Корректная передача значения в функции, принимающие указатель)
NullableType<int> globIntParam2(intVarPtr); // А заодно пусть будет и конструктор копирования

Мне нужна простая обёртка над встроенными простыми С-типами, позволяющая присваивать им NULL-значение, нечто вроде int? или double? в C#, без поддержки присвоения NULL «всему, что угодно». (На всякий случай, скажу, что речи об использовании фич C++11 и выше быть не может). Немного погуглив, я нашёл парочку похожих реализаций, но меня смущало отсутствие в них всех нужных мне возможностей и я написал свою реализацию nullable-обёртки.

Если я прав, то нечто подобное нужно во многих C++-проектах. Поэтому тяжело представить, что до сих пор гугл не выдаёт простое решение по этому вопросу.

Моя обёртка имеет проблему — её нельзя бездумно использовать со строками, т.к. при инициализации “обёрнутой” в неё строки значением NULL нужно как-то сделать так, чтобы при попытке конвертации в string получить из NULL пустую строку. В комментариях к исходникам есть вариант быстрого решения (проверено в gcc и clang, всё ок), но он может не всем понравиться. Ещё эту проблему можно решить для MFC в частном порядке, добавив поддержку CString, которые в нём используются вместо обычного string, но я не стал этого делать, чтобы не привязывать её к MFC, да и до сего момента мне просто не потребовалась запись NULL-значения в БД вместо пустой строки.

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

Исходный код описанного nullable-класса находится тут.
Tags:
Hubs:
+1
Comments 10
Comments Comments 10

Articles