Как уменьшить код реализации множества классов-обёрток? - PullRequest
6 голосов
/ 23 октября 2019

Я занимаюсь разработкой библиотеки с некоторыми классами, назовем их C1, C2 and ... Cn. Каждый из этих классов реализует несколько интерфейсов, т.е. I1, I2, ... Im. (n> m). Связь между объектами в библиотеке сложная, и я должен предоставить некоторый API для пользователей моей библиотеки, чтобы получить доступ к этим объектам с помощью интеллектуальных указателей.

После некоторых обсуждений я обнаружил, что возвращение общих указателей пользователям библиотеки не является хорошей идеей, потому что в этом случае я не могу гарантировать, что объект может быть удален точно в памяти моей библиотеки. Возврат слабых указателей имеет ту же проблему, потому что, если пользователь API .lock() использует слабый указатель и где-то хранит полученный общий указатель, я снова столкнусь с той же проблемой.

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

class Wrapper_C1 : public I1
{
   std::weak_ptr<C1> mC1;
public:
   Wrapper_C1() = delete;
   Wrapper_C1(const std::weak_ptr<C1> & c1) : mC1(c1)
   {
   }

   int method1_C1(int x)
   {
       if (auto sp = mC1.lock())
       {
           sp->method1_C1(x);
       }
       else
       {
            throw std::runtime_error("object C1 is not loaded in the lib.");
       }
   }

   void method2_C1(double y)
   {
       if (auto sp = mC1.lock())
       {
           sp->method2_C1(y);
       }
       else
       {
            throw std::runtime_error("object C1 is not loaded in the lib.");
       }
   }

   // The same for other methods
};

Как видите, все классы-оболочки имеют одну и ту же реализацию. Каков наилучший способ уменьшить код ВСЕХ этих классов-обёрток? Есть ли способ избежать повторения подобных кодов?

Ответы [ 3 ]

5 голосов
/ 23 октября 2019

Если вы отбросите наследование в оболочке, вы можете сделать что-то вроде следующего, чтобы разложить все оболочки:

template <typename T>
class Wrapper
{
private:
   std::weak_ptr<T> m;
public:
   Wrapper() = delete;
   Wrapper(const std::weak_ptr<T> & w) : m(w) {}

   auto operator -> () /* const */
   {
       if (auto sp = m.lock())
       {
           return sp;
       }
       else
       {
            throw std::runtime_error("object is not loaded in the lib.");
       }
   }
};
4 голосов
/ 23 октября 2019

Лучшее, что вы можете сделать, не прибегая к макросам (которые также не помогли бы здесь, чтобы полностью решить вашу проблему, нам понадобится какое-то статическое отражение), это исправить эти повторения:

if (auto sp = mC1.lock())
{
    sp->method1_C1();
}
else
{
     throw std::Exception("object C1 is not loaded in the lib.");
}

ЧтоЯ вижу, что вы можете легко превратить его в шаблонную функцию, например:

template<class T, class R, class... Args>
R call_or_throw(const std::weak_ptr<T>& ptr, const std::string& error, R (T::*fun)(Args...), Args... args) {
    if (auto sp = ptr.lock()) 
    {
        return std::invoke(fun, *sp, args...);
    }
    else 
    {
        throw std::runtime_error(error.c_str());
    }
}

Чем вы можете использовать его так:

int method1_C1(int x)
{
    return call_or_throw(mC1, "object C1 is not loaded in the lib.", &C1::method1_C1, x);
}

void method2_C1(double y)
{
    return call_or_throw(mC1, "object C1 is not loaded in the lib.", &C1::method2_C1, y);
}

Вы можете даже сделать из него макрос

2 голосов
/ 23 октября 2019

Использование смарт-указателей для узлов дерева / графа далеко не идеально. Деструкторы узлов дерева уничтожают умные указатели на дочерние узлы, а те, в свою очередь, вызывают деструкторы дочерних узлов, что приводит к рекурсии, которая может переполнить стек, когда деревья глубоки или размер стека мал.

Альтернативный дизайн -иметь класс дерева, который управляет временем жизни его узлов и использует простые указатели, а-ля std::map. И имейте правило, что удаление узла лишает законной силы указатели и ссылки на удаленное поддерево.

Такой дизайн прост, надежен и наиболее эффективен во время выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...