3

I have many classes that use std::tie to make a tuple out of all the class attribute and use it to implement the == operator. They look like this:

class S
{
    int number;
    std::string text;
    // and many other attributes...
    
    auto tied() const noexcept { return std::tie(/* list all attributes here */); }
    
    bool operator==(const S &o) const noexcept
    {
        return tied() == o.tied();
    }
};

The classes have similar methods but very different attributes, so I want to create a base class they all inherit from and I want to include this bit of comparison in the base class.

However, since I can't define a virtual method returning auto, I'm struggling to write an abstract generic tied() method that should make a tuple out of all a derived class's attributes no matter how many of them or of which types they are.

Is this feasible?

Note: All attributes are either of trivial type or std::strings.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Haythem
  • 31
  • 4
  • 6
    No it's not feasible. This sounds like an XY problem. What is the real problem are you trying to solve? No, not the one about making tuples from class members, but the problem to which you believe the solution is to make tuples from class members, so that's what you're asking about. Perhaps if you explain your original problem, a different solution will become very apparent. – Sam Varshavchik Nov 07 '20 at 17:08
  • 2
    If you can use C++20, the [default](https://en.cppreference.com/w/cpp/language/default_comparisons) available for `operator==` could be what you're looking for. – Nathan Pierson Nov 07 '20 at 17:17
  • There are "hacks", like https://stackoverflow.com/q/39768517/1774667 , but not in general. And the children have to do the work themselves to add their methods. As noted `operator==(...) = default` might do what you want. – Yakk - Adam Nevraumont Nov 07 '20 at 17:21
  • Unlike python, C++ has no attributes interface (https://stackoverflow.com/questions/9058305/getting-attributes-of-a-class). C++ would carry so much more weight to accomplish this - and it is not the target solution for the language. – natersoz Nov 07 '20 at 17:21
  • @SamVarshavchik I have a dozen of classes with the same pattern: many different attributes and a == operator. The objects of these classes represent sets of parameters, and when an full update happen, I would only interested in the objects where at least a parameter has been changed, hence the comparison. I'm trying to find a quick way to implement this, where if in the future, I need to add a new set of parameters, I'll only a a new class with all the new parameters as attributes. Then inherit from the base class that will give me the same comparison mechanism. – Haythem Nov 07 '20 at 17:26
  • Well, you're in luck: I just happened to have worked on a library that also had dozens of classes, with many different class members, but had a common set of operations to be implemented for all of them. C++ can do everything, but not everything must be done in C++. In my case, I rolled up my sleeves and defined all my classes and their members in XML, then used a stylesheet to robo-generate the class declarations as well as the common C++ code for all of them. In your case, you would generate a method that returns a `std::tuple` of its class members. Mission accomplished. – Sam Varshavchik Nov 07 '20 at 17:30
  • @NathanPierson that looks interesting. Too bad I'm stuck with C++14 – Haythem Nov 07 '20 at 17:31

2 Answers2

1

Is there a generic way to make a tuple out of all class attributes?

There is no way to generate a list of members. That is a language feature that requires more reflection capabilities than C++ has. At best you can have a macro that simultaneously generates the member declarations and the function generating the tuple. See Boost Fusion for an implementation of that idea.

Of course, you are not limited to the C pre processor for meta programming. You can use any langauge to generate C++ source if you don't mind complicating the build process.

I'm struggling to write an abstract generic tied()

There is no way to write a virtual member function that would return a different tuple for derived instances.

Is this feasible?

No.

and use it to implement the == operator.

Since C++20, you can use defaulted <=> to generate all comparison operators. Prior to that, you need the boilerplate.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

If there is a bounded number of elements, you can use CRTP and https://stackoverflow.com/a/39779537/1774667 to find the number of elements.

Using structured binding you can then build a tuple out of them.

This can be written in the CRTP base.

template<class T>
struct implement_equals {
  friend bool operator!=(implement_equals const& lhs, implement_equals const& rhs ) {
    return !(lhs==rhs);
  }
  T const& self() const {
    return *static_cast<T const*>(this);
  }
  friend bool operator==(implement_equals const& lhs, implement_equals const& rhs) {
    constexpr std::size_t count = construct_airity<T>;
    return make_tie_from<count>{}(lhs.self()) == make_tie_from<count>{}(rhs.self());
  }
};

next write make_tuple_from.

template<std::size_t>
struct make_tie_from;

template<>
struct make_tie_from<1> {
  template<class T>
  auto operator()( T const& t ) const {
    auto const&[e0] = t;
    return std::tie(e0);
  }
};

then write 20 of those.

There are libraries that do this already for you you can find with a bit of googling. None of them are perfect, and they all require that the thing you are making a tie of expose the data publicly.

If you want to hide that data, create a struct that contains the data publicly to get the tie, and expose some way for implement_equals to get access to that struct (friend, whatever).


In you cannot do even this crippled for of reflection.

You can write implement_equals, but you have to manually write the tie. It is better than a virtual function, however.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524