1
votes

In Haskell, typeclasses allow for you to elegantly overload functions based on return type. It is trivial to replicate this in C++ for cases where both the arguments and the return type are overloaded, using templates (example A):

template <typename In, typename Out> Out f(In value);

template <typename T> int f<T, int>(T value) {
   ...
}

Which corresponds to Haskell's:

class F a b where
    f :: a -> b

You can even overload just the return type on most functions (example B):

template <typename Out> Out f(SomeClass const &value);

template <> inline int f(SomeClass const &value) {
    return value.asInt();
}

template <> inline float f(SomClass const &value) {
    return value.asFloat();
}

Which corresponds to something like:

class F a where
    f :: SomeData -> a

But what I would like to be able to do is modify this last example to overload on higher-order types, namely templated structs in C++. That is, I'd like to be able to write a specialization akin to the following Haskell:

data Foo a = Foo a

instance F (Foo a) where
    f someData = Foo $ ...

How would one go about writing a template with this functionality (is it even possible)?


For reference, I'm intending to use this to write template functions for Lua/C++ bridge. The idea is to bridge between Lua and C++ functions by having an overloaded function interpretValue that automatically pushes or converts from the Lua stack. For simple types that have a direct built-in Lua representation, this is easy enough using code such as example B.

For more complicated types, I'm also writing a template <typename T> struct Data to handle memory management for objects (bridging between Lua's GC and a C++ side refcount), and I was hoping to be able to overload interpretValue so that it can automatically wrap a userdata pointer into a Data<T>. I tried using the following, but clang gave a "function call is ambiguous" error:

template <typename U> inline U &interpretValue(lua_State *state, int index) {
    return Data<U>::storedValueFromLuaStack(state, index);
}

template <typename U> inline Data<U> interpretValue(lua_State *state, int index) {
    return Data<U>::fromLuaStack(state, index);
}

Thanks!

1
Does template <typename T> Foo<T> f(const SomeClass& value) not work for you?Brian Bi
@Brian it does not, unfortunately. I believe that's equivalent to the last code snippet I posted, which gives an ambiguous function call error.gntskn

1 Answers

2
votes

Well, you can write one function:

template <class U>
interpretValueReturnType<U> interpretValue(lua_State *state, int index)
{
    return interpretValueReturnType<U>(state, index);
}

Then, you need to write this return type with casting operators, so, you get what you want:

template <class U>
class interpretValueReturnType
{
public:
    interpretValueReturnType(lua_State *state, int index) : state(state), index(index) {}

    operator U& () &&
    {
        return Data<U>::storedValueFromLuaStack(state, index);
    }

    operator Data<U> () &&
    {
        return Data<U>::fromLuaStack(state, index);
    }
private:
    lua_State *state;
    int index;
};

See ideone:

int main() {
    lua_State *state;
    int& a = interpretValue<int>(state, 1);
    Data<int> b = interpretValue<int>(state, 1);
}

This funny && at the end of operators declarations are for making a little hard to store result of this function and use it later - like here:

    auto c = interpretValue<float>(state, 1);
    float& d = c; // not compile

One'd need to use std::move because && means that function can be used only for rvalue references:

    auto c = interpretValue<float>(state, 1);
    float& d = std::move(c);