Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 568
Gupta Avatar asked Oct 23 '19 08:10

Gupta


2 Answers

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.");
       }
   }
};
like image 115
Jarod42 Avatar answered Oct 06 '22 00:10

Jarod42


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

like image 43
bartop Avatar answered Oct 05 '22 23:10

bartop