te icon indicating copy to clipboard operation
te copied to clipboard

Looking for a mechanism to make poly comparable, and/or to be able to do runtime casts

Open suy opened this issue 1 year ago • 0 comments

Expected Behavior

I expected to be able to find a way to make the poly interface comparable, so one could compare the value stored in it. It's reasonably easy to add a way to check if the types are the same (by adding a function to make them return the type_index of type inside the poly), then return false in the comparison operator when they are not, but I've not been able to find a way to compare the same types by calling the operator== of the right one when those are the same. Example:

#include <iostream>
#include <typeindex>

#include "te.hpp"

using std::cout;
using std::endl;

namespace te = boost::te;

struct One {
     bool operator==(const One&) const {
         return true;
     }
};

struct Two {
     bool operator==(const Two&) const {
         return true;
     }
};


struct Comparable : te::poly<Comparable> {
    using te::poly<Comparable>::poly;

    bool operator==(const Comparable& other) const {
        auto body = [](auto const& self, Comparable const& other) {
            // `self` is the "downcast" type (One/Two), but `other` is
            // Comparable, the interface. We need to figure out how to get the
            // right type.

            // This doesn't really work, as the types are never the same.
            // if constexpr (std::is_same_v<decltype(self), decltype(other)>)
            //     return self == other;
            // return false;

            // This doesn't compile, as One or Two can (and should) only compare
            // with themselves, but `other` is of type Comparable.
            // return self == other;

            const bool sameType = other.type() == std::type_index(typeid(self));

            // Not ideal, as we know for sure that we are not the same when the
            // type is not the same, but we don't know if two objects of the
            // same type are the same. We need to cast `other` to the type of
            // `self`. Now `other` is Comparable, while `self` is the "real"
            // object put into a Comparable (e.g. One, Two).
            // return sameType;

            // The next lines are the further I've been able to get to.
            if (!sameType)
                return false;

            // Attempt to cast `other` to the same type of `self`.
            using SelfType = decltype(self);

            auto innerBody = [] (auto const& innerSelf, const SelfType& innerOther) {
                if constexpr (std::is_same_v<decltype(innerSelf), SelfType>)
                    return false;
                else
                    return innerSelf == innerOther;
            };
            // Crashes. I tried to swap `other` and `self` with respect to the
            // other te::call, so I could get the `Comparable const& other` cast
            // to the right type inside the poly, and use the `operator==` of
            // `One` or `Two`.
            return te::call<bool>(innerBody, other, self);
        };

        return te::call<bool>(body, *this, other);
    }

    std::type_index type() const {
        return te::call<std::type_index>([](auto const& self) {
            return std::type_index(typeid(self));
        }, *this);
    }
};

int main()
{
    Comparable one = One{};
    Comparable two = Two{};
    cout << one.type().name() << endl;
    cout << two.type().name() << endl;
    cout << "one==one " << (one == one) << endl;
    cout << "one==two " << (one == two) << endl;
    cout << "two==two " << (two == two) << endl;
}

Actual Behavior

With the code as in the cample above, it crashes at runtime, and I could not properly understand why.

Alternatively, I would consider that adding something like te::cast<T>, with a similar behavior as any_cast, would make sense. It would allow one to do the usual operations that one can do with std::any and "normal" polymorphism. The AnyAny library seems to provide that functionality (both the cast, and a default way to make the polymorphic value comparable via a built-in API for making it provide the operator).

Steps to Reproduce the Problem

Just trying the example above.

Specifications

  • Version: latest te.hpp (27465847fe489d33a91014488284210f183cb502)
  • Platform: Linux
  • Subsystem: GCC or clang

Thank you!

suy avatar Oct 26 '22 13:10 suy