Question

So I have a resource manager that is basically a dictionary of void* objects that have some metadata describing their type (basic metadata like a string). I have a template method right now that returns an object:

class ResourceManager
{
    template<typename Type>
    Type RetrieveResource( Text name );
};

My question is how can I make this method not a template method? I've done some research into trailing return types and using that funky syntax. The idea is that at some point I have the data as the correct type and I want to return it as that type without additional casting on the end user's part.

I'm shooting for something like this:

auto RetrieveResource( Text name)
{
    return _dictionary[ name ]; // There's more to this, but imagine it returns varying type objects.
}

I've given some thought to implementing a Boost::Any type of object, but it's kind of complicated (not that I'm lazy). I'm trying to avoid template syntax on the end user's part.

Any help would be great. Also I cannot use Boost library structures like variant or any (project specs). Thanks in advance!

I understand I'm kind of asking for magic here, but I figured that's what S/O is for: finding magic. If it's not possible, I understand. But if someone has an innovative solution, that would be awesome.

Was it helpful?

Solution 2

I'm afraid that would be to ambiguous. You can't overload by return type and you can't use auto as the return type.

OTHER TIPS

In addition to the other answers, you could do something like this, assuming that every Text key corresponds uniquely to a resource with a known, different Type:

class BaseText
{
    // constructors that take const char *, std::string, etc.
    // .. whatever else you currently have in class Text
};

template<typename Type>
class Text : public BaseText
{
   // constructors that take const char *, std::string, etc.
};

Then change ResourceManager to be:

class ResourceManager
{
    template<typename Type>
    Type RetrieveResource( Text<Type> name );
};

Note this still requires compile-time polymorphism, but means that as long as your user can obtain the correctly-typed version of Text<Type>, they can use it to obtain the correctly typed resource without having to explicitly provide the Type argument again, so it'll look like a normal function call.

This works easily if the set of possible Text<Type> objects can be statically declared and initialized with the right types, which I'm not sure is true in your case. If, instead, the user must be able to create them at runtime, though, they'll need to know the correct type to create, so it just pushes the problem back a bit further: however, it might be convenient enough if the same Text<Type> object is going to be used over and over again.

Note that you're also out of luck if you or the client need to store different Text<Type> objects together in some kind of data structure, since that will require some sort of type erasure (i.e. upcasting to BaseText). So this is somewhat of a specialized solution but it's possible it could be useful, depending on how things are structured (I really can't know without seeing more of your code).

EDIT: As per the comment below, it seems like the type of the object is determined by the client when they insert an object, so you could possibly do something like this:

class ResourceManager
{
    BaseText PutResource( const BaseText& name, BaseResource& resource );

    template<typename Type>
    Text<Type> PutResource( const BaseText& name, Type& resource );

    BaseResource& RetrieveResource( const BaseText& name );

    template<typename Type>
    Type& RetrieveResource( const Text<Type>& name );
};

So the client is required to hold on to the unique typed key object in order to retrieve the resource later; if they choose to type erase it by upcasting it back to BaseText, then they'll be responsible for recovering the type information correctly, either by downcasting the key before retrieval or downcasting the resource afterwards.

Note that this is just an extra option on top of what you will have to do anyway: if you want to provide access to resources using a non-type-specific key, the only way to do so is by returning a non-specific resource type (i.e. BaseResource, boost::any, boost::variant<...>, or one of their moral equivalents.) However, creating a type-specific subclass of BaseText allows you to obtain a correctly typed resource, using the exact same syntax (i.e. with no explicit template parameter required). Furthermore, this allows the following usage:

// tree is a resource of type `Tree`
auto key = manager.PutResource("Tree01", tree);

// ...

const BaseText& basekey = key; // lose type information

// ...

// these are all equivalent
Tree& tree = dynamic_cast<Tree&>(manager.RetrieveResource("Tree01"));
Tree& tree = dynamic_cast<Tree&>(manager.RetrieveResource(basekey));
Tree& tree = manager.RetrieveResource(key); // no casts required
Tree& tree = manager.RetrieveResource<Tree>("Tree01")

The dynamic_cast versions are basically what you'll need to do without these templated overloads, but the others two versions are extra options on top of that that you can use with this approach without any runtime cost.

I think, that the best choice will be returning some base class pointer. The minus is that you force anyone to derive from it, but with smart architecture of base class there could be more pluses

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top