Home > Software engineering >  C Conversion operator with multiple inheritance
C Conversion operator with multiple inheritance

Time:07-31

Consider this case with multiple (implementation) inheritance with mixin pattern:

#include <string>
#include <iostream>

template <typename... Bases>
struct Overloads : public Bases... {};

struct Human {};

struct Animal {};

struct Named {
  std::string name_;
  void setName(const std::string& name) {
    name_ = name;  
  }
  const std::string& getName() const noexcept { return name_; }
};

template <typename OverloadsType>
struct Actor : public OverloadsType {

  Actor() : OverloadsType() {}

  template <typename OtherOverloads>
  Actor(const Actor<OtherOverloads>& other_actor) {
      // ???????
      this->setName(other_actor.getName());
  }

};

int main() {
    Actor<Overloads<Human, Named>> named_human;
    named_human.setName("Bob");
    std::cout << named_human.getName() << '\n';
    Actor<Overloads<Animal, Named>> named_animal;

    Actor<Overloads<Animal, Named>> animal_once_a_human (named_human);
    std::cout << animal_once_a_human.getName() << '\n';    
}

The code works correctly, printing two Bobs: Link

I want two things

  1. Make the conversion operator compiles even when OverloadsType and OtherOverloads aren't derived from Named (this->setName(other_actor.getName()); should be ignored or not compiled at all)

  2. Generalize "transferring" information from (common) base classes, not only name

How can I do this?

CodePudding user response:

Here's a basic blueprint. This can be further refined so that the concept actually checks that getName() return a std::string.

#include <string>
#include <iostream>

template<typename T>
concept has_a_name = requires(T &t) {

    { t.getName() };
};

template<typename T, typename U>
void give_name(const T &t, U &u)
{
}

template<has_a_name T, typename U>
void give_name(const T &t, U &u)
{
    u.setName(t.getName());
}


struct tom {
    std::string getName() const
    {
        return "Tom";
    }
};

struct jerry {};

struct cartoon {

    void setName(const std::string &s)
    {
        std::cout << s << "\n";
    }

    template<typename T>
    cartoon(const T &t)
    {
        give_name(t, *this);
    }
};

int main()
{
    tom Tom;
    jerry Jerry;

    cartoon mgm{Tom}, mgm2{Jerry};
    return 0;
}

As far as generalizing this goes, any possible "generic" way of defining getters and setters will either be even longer than this, or use arcane, cryptic, difficult to read templates that end up expressing very simple operations.

A simple concept that defines each class that implements a particular getter, and a pair of template functions that select a stub or the real deal, via simple overload resolution, is easy to read, understand and follow.

CodePudding user response:

I want two things

  1. Make the conversion operator compiles even when OverloadsType and OtherOverloads aren't derived from Named (this->setName(other_actor.getName()); should be ignored or not compiled at all)

  2. Generalize "transferring" information from (common) base classes, not only name

How can I do this?

So, if the source also has the base class, it should be copied, and otherwise we should use a default constructed instance. This is straightforward.

We can define a function to extract that initializer, a copy of a base if there is one, otherwise a default constructed instance:

template <std::semiregular T, typename U>
constexpr auto extract(const U& u) -> T {
  if constexpr (std::derived_from<U, T>) {
    return u;
  }
  else {
    return T();
  }
}

Then we can use that to initialize the "overloads":

template <typename... Bases>
struct Overloads : public Bases... {
  Overloads() = default;

  template <typename OtherOverloads>
  Overloads(const OtherOverloads& other_overloads) : Bases{extract<Bases>(other_overloads)}... {}
};

Then we can use that logic to initialize the actor:

  template <typename OtherOverloads>
  Actor(const Actor<OtherOverloads>& other_actor) : OverloadsType(other_actor) {}

See https://godbolt.org/z/ofjPb8x75

  • Related