Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
suy opened this issue Oct 26, 2022 · 0 comments

Comments

@suy
Copy link

suy commented Oct 26, 2022

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 (2746584)
  • Platform: Linux
  • Subsystem: GCC or clang

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant