Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
110 views
in Technique[技术] by (71.8m points)

api - C++ Future handling

I'm building a C++ program which is using API to try and acquire some data about packages tracking info and some of those methods are returning futures, and if the future is fulfilled, I want to return it to the main program. Consider the following:

std::future<ApiClass> myMethodReturningFuture() {
    /* some processing */
    std::future<ApiClass> future = api.call();
    return future;
}

My question is how to correctly handle the future (eg. check if it contains correct info, calling future.valid() seems to always return 1). Also, if the future is not fully valid, how to return NULL or nullptr from this function ? Let's consider this situation:

std::future<ApiClass> myMethodReturningFuture() {
    /* some processing */
    try {
        std::future<ApiClass> future = api.call();
        return future;
    } catch (std::exception& e) {
        return nullptr // or NULL;
    }
}

The snippet above doesn't compile. Even if I make a function to return a pointer, I get an error about "using deleted function std::future". Something like this:

std::future<ApiClass>* myMethodReturningFuture() {
    /* some processing */
    try {
        std::future<ApiClass>* future = &api.call();
        return future;
    } catch (std::exception& e) {
        return nullptr // or NULL;
    }
}
question from:https://stackoverflow.com/questions/65906612/c-future-handling

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

A future is basically a wrapper around some shared state. valid is telling you whether the future actually has some shared state or not.

A future will have no shared state if it was default constructed, or if its shared state has already been retrieved, or if it was the source of a move, so some other future now owns the shared state this one used to.

At least as I understand what you're writing, chances are pretty good that you really want to just return the future as is, and have no reason to check whether it's valid before you return it--in particular, if you receive a future that's not valid, the equivalent of a "null future" that you'd return to signal that would be a default-constructed future that would signal "not present" ...by its valid returning false. In other words, you'd signal the lack of a future by returning exactly the same sort of one as you received in the first place.

I suppose you could separate out the signaling of "not present" from the future if you really wanted to. For example, you could define your function something like this:

std::optional<std::future<ApiClass>> myMethodReturningFuture() {

In an ideal1 world with every class fully decomposed and having only a single responsibility, std::future probably wouldn't exist--you'd instead have something like std::optional<std::shared_state<T>> in its place, and the optional part would be responsible for saying whether it's present or not, and the shared_state would only deal with being shared state. But that's not the world we live in, so future's valid is basically acting like the optional, and telling you whether the shared state is present or not.

If you were going to do this, you'd need to account for the fact that a future is moveable but not copyable, so when you assign one to a variable, you need to use std::move to get it back out of that variable. For example, here's some trivial (but complete) code to do the check and return an std::optional<std::future<T>>:

#include <optional>
#include <future>

struct ApiClass {
    int i;
};

struct Api { 
    std::future<ApiClass> call() const {
        // create and return a (trivial) future.
        return std::async(std::launch::async, []{ return ApiClass {1}; });
    }
};

std::optional<std::future<ApiClass>> myMethodReturningFuture() {
    Api api;
    // here we create a `future` object, so it's an lvalue, not an rvalue
    std::future<ApiClass> future {api.call()};
    // so when we want to create our optional<future>, we need to tell the
    // compiler to move instead of copying the object we created:
    return future.valid() == 1 ? std::make_optional(std::move(future)) : std::nullopt;
}

1. Well, ideal from one viewpoint. Frankly, I'm not sure this design would turn out so ideal in real use. Then again, I'm not sure `future` is entirely ideal in real use either.

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...