Skip to content

when_all() does not compile with move-only objects #146

@zurrutik

Description

@zurrutik

Hi,

I have the following function defined on my code:

template <typename Resource>
auto load()
{
	std::vector<std::string>assetFilenames{ "img1.jpg", "img2.jpg" };

	std::vector<stlab::future<Resource>> tasks;

	//1 task per file
	for( auto i = 0u; i < assetFilenames.size(); ++i )
	{
		tasks.push_back( stlab::async( stlab::default_executor, [f = std::move( assetFilenames[i] )]
		{
			return Resource{ f };
		} ) );
	}

	return stlab::when_all( stlab::default_executor, []( auto&& results )
	{
		return results;

	}, std::make_pair( tasks.begin(), tasks.end() ) );
}

Resource represents a class that is move-only, my engine relies A LOT in objects like this, e.g. Texture, VertexBuffer etc are all move-only objects. For illustration purposes I am going to define a move-only class called Dummy:

struct Dummy
{
    Dummy() = default;
    ~Dummy() = default;
    
    explicit Dummy( const string& filename )
        : filename{ filename }{}
     
     Dummy( Dummy&& ) = default;  
     Dummy& operator=( Dummy&& ) = default;    

public:    
    string filename;
};

If I do the following with a copyable type like std::string, things work perfect( NOTE: I know we shouldn't use blocking_get(), but it's just for illustration purposes )

auto cache = blocking_get( load<string>( ) );

Unfortunately if I use a move-only object the program fails to compile

auto cache = blocking_get( load<Dummy>( ) );

The compilation error happens here

template <typename R, typename C>
struct create_range_of_futures {
    template <typename S, typename F, typename I>
    static auto do_it(S&& s, F&& f, I first, I last) {
        assert(first != last);

        auto context = std::make_shared<C>(std::forward<F>(f), std::distance(first, last));
        auto p = package<R()>(std::move(s), [_c = context] { return _c->execute(); });

        context->_f = std::move(p.first);

        size_t index(0);
        std::for_each(first, last,
                      [&index, &context](auto item) { attach_tasks(index++, context, item); }); //Compilation error here: It tries to copy a future<Dummy> into item, but it doesnt succeed

        return std::move(p.second);
    }
};

Of course I could use shared_ptr as a workaround:

template <typename Resource>
auto load2()
{
	std::vector<std::string>assetFilenames{ "img1.jpg", "img2.jpg" };

	std::vector<stlab::future<shared_ptr<Resource>>> tasks;

	//1 task per file
	for( auto i = 0u; i < assetFilenames.size(); ++i )
	{
		tasks.push_back( stlab::async( default_executor, [f = std::move( assetFilenames[i] )]
			{
				return make_shared<Resource>( f );
			} ) );
	}

	return when_all( default_executor, []( auto&& results )
	{
		return results;

	}, make_pair( tasks.begin(), tasks.end() ) );
}
vector<Dummy> cache;

for( auto& elem : blocking_get( load2<Dummy>() ) )
     cache.emplace_back( move( *elem ) );

...but obviously this solution is not very elegant :(. Could you please let me know if there is a better solution to this problem?

Thanks,

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions