How to reduce the implementation code of lots of wrapper classes?

血红的双手。 提交于 2019-12-10 13:33:38

问题


I am developing a library with some classes, let's call them C1, C2 and ... Cn. Each of these classes realize some interfaces, i.e. I1, I2, ... Im. (n > m). The relationship between objects in the library is complex and I have to provide some API for my library users to access these objects using smart pointers.

After some discussions, I found that returning shared pointers to the library users is not a good idea, because in that case I cannot make sure that the object can be removed precisely in my library's memory. Returning weak pointers have the same issue, because if the user of the API .lock()s the weak pointer and keep the resulted shared pointer somewhere, I will face the same problem again.

The final idea I have, is to expose some kind of wrappers for the weak pointers. A wrapper class can be something like this:

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

As you see, all of this wrapper classes, share the same implementation. What is the best way to reduce the code of ALL of these wrapper classes? Is there anyway to avoid repeating the similar codes?


回答1:


If you drop inheritance in the wrapper, you might do something like the following to factorize all wrappers:

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.");
       }
   }
};



回答2:


The best you can do without resorting to macros (which also wouldn't help here, to fully resolve your problem we would need some kind of static reflection) is fix these repetitions:

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

What I see you can easily reduce it to template function like this one:

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());
    }
}

Than you can use it like that:

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

You can even make macro out of it




回答3:


Using smart-pointers for tree/graph nodes is less than ideal. The tree node destructors destroy the smart-pointers to child nodes and those in turn call child node destructors resulting in recursion which may overflow the stack when the trees are deep or available stack size is small.

An alternative design is to have a tree class that manages the lifetime of its nodes and uses plain pointers, a-la std::map. And have a rule that removing a node invalidates pointers and references to the removed sub-tree.

Such a design is simple, robust and most efficient at run-time.



来源:https://stackoverflow.com/questions/58518574/how-to-reduce-the-implementation-code-of-lots-of-wrapper-classes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!