托管C ++中未来和承诺的替代方法是什么

时间:2017-07-20 20:23:23

标签: c++-cli clr

使用/ clr标志编译托管C ++代码时,编译器不允许使用include。我试图将我的非托管C ++代码移植到托管C ++环境中。我看到C#有替代Task和TaskCompletionSource替换期货和承诺,但我没有看到托管C ++中有这些选项。我需要与一些C ++非托管库执行互操作,因此我无法完全切换到C#。我之间仍然需要一个C ++层。如何在托管C ++中实现未来/承诺功能?

以下是C ++中非托管代码的示例,它在没有/ clr标志的情况下进行编译:

int Foo(std::future<int> &fur) {
    int result = 1;
    int value = fut.get();
    // Do something with value

    return result;
}   

int main() {
    int x;
    std::promise<int> p;
    std::future<int> f = p.get_future();
    std::future<int> fut = std::async(Foo, std::ref(f));
    int val = 1;
    p.set_value(val);
    x = fut.get();
}    

我希望能够在C ++ / CLI中执行此操作

2 个答案:

答案 0 :(得分:2)

更新一些早期评论在此重新制定):

即使我们选择公共语言运行时支持(C ++ - Cli,CLR),许多标准C ++库也可以免费使用。但其中一些是不可用的,例如被质疑的那些。

如果我们添加这样的标题,我们会收到以下错误:

&#39;使用/ clr或/ clr:pure进行编译时,不支持<future>。 &#39;

这对我们来说意味着这样的代码可以保留在一个单独的dll中,或者我们必须重构我们的代码或者我们需要一个C#实现所提到的库,如this一个。

<强>答案

问题本身包含正确的答案,因此可以使用以下方法实现未来和素数:

  • Task<T>是未来(或单位回归未来的任务),
  • TaskCompletionSource<T>是一个承诺,

也显示在帖子here中。这意味着我们可以简单地search C#替换未来和承诺,然后将它们转换为C ++ - Cli。

在我的回答中,我只展示了如何翻译和使用Task以及内置代表的一些要点。任务本身很简单:

    #include "stdafx.h"

    using namespace System;
    using namespace System::Threading::Tasks;

    public ref class MyActions
    {
    public:
        MyActions()
        {
            // lambdas are not allowed for managed class so we use built in delegates
            auto t = gcnew Task(gcnew Action<Object^>(task1),this);
        }

    public:
        static void task1(Object^ o)
        {
            // TODO:
            printf("Hello World! [c++-cli] and [win32]");
        }
    };

正如您所看到的,C ++ - Cli语法与c#非常相似,并且您仍然可以使用大多数旧版本的#c; C ++。

注意:我们仍然应该将类定义和实现分开(myactions.h和myactions.cpp)

答案 1 :(得分:0)

C ++ / CLI不允许包含任何标准标题<mutex><future><thread>&amp; <condition_variable>(VS2017中的BTW,您可以使用<atomic>)。它也不允许使用微软提供的concurrency runtime

由于您使用的是C ++ / CLI,我认为可移植性不是问题。 我的建议是你在windows API周围使用瘦包装器来推送自己的互斥锁,condition_variable,future和promise。

前段时间我写了一些代码就是这样做的。它并不像听起来那么难。

注意:

  • 此附加代码无意以任何方式符合标准。
  • srwlock是std :: mutex的替代品,甚至可以用于 std :: lock_guard / std :: unique_lock。
  • srwcondition_variable是std :: condition_variable。
  • 的替代品
  • unique_srwlock是std :: unique_lock的替代品(用于 srwcondition_variable)。
  • srfuture&amp; srpromise是std :: future&amp;的替代品。的std ::承诺。

#include <windows.h>

class srwlock
{
    SRWLOCK _lk{};
public:
    void lock()
    {
        AcquireSRWLockExclusive(&_lk);
    }
    void unlock()
    {
        ReleaseSRWLockExclusive(&_lk);
    }
    void lock_shared()
    {
        AcquireSRWLockShared(&_lk);
    }

    void unlock_shared()
    {
        ReleaseSRWLockShared(&_lk);
    }
    SRWLOCK* native_handle()
    {
        return &_lk;
    }
    srwlock() = default;
    srwlock(const srwlock&) = delete;
    srwlock& operator=(const srwlock&) = delete;
    srwlock(srwlock&&) = delete;
    srwlock& operator=(srwlock&&) = delete;
};


template <typename _Lk>
class unique_srwlock
{
    _Lk& _lk;
    friend class srwcondition_variable;
public:
    void lock()
    {
        _lk.lock();
    }
    void unlock()
    {
        _lk.unlock();
    }

    unique_srwlock(_Lk& lk) : _lk(lk)
    {
        _lk.lock();
    };

    ~unique_srwlock()
    {
        _lk.unlock();
    };

    unique_srwlock(const unique_srwlock&) = delete;
    unique_srwlock& operator=(const unique_srwlock&) = delete;
    unique_srwlock(unique_srwlock&&) = delete;
    unique_srwlock& operator=(unique_srwlock&&) = delete;

};

    enum class srcv_status
{
    timeout, no_timeout
};

class srwcondition_variable
{
    CONDITION_VARIABLE _cv{}; 
public:
    void wait(srwlock& lk)
    {
        VERIFY_TRUE(SleepConditionVariableSRW(&_cv, lk.native_handle(), INFINITE, 0));
    }
    void wait(unique_srwlock<srwlock>& lk)
    {
        VERIFY_TRUE(SleepConditionVariableSRW(&_cv, lk._lk.native_handle(), INFINITE, 0));
    }

    srcv_status wait_for(unique_srwlock<srwlock>& lk, const std::chrono::milliseconds& timeout_duration)
    {
        auto val = SleepConditionVariableSRW(&_cv, lk._lk.native_handle(), static_cast<DWORD>(timeout_duration.count()), 0);

        if (val != 0)
        {
            return srcv_status::no_timeout;
        }
        else
        {
            if (GetLastError() == ERROR_TIMEOUT)
            {
                return srcv_status::timeout;
            }
            else
            {
                throw std::runtime_error("wait_for unexpected return value in SleepConditionVariableSRW");
            }
        }
    }

    void notify_one()
    {
        WakeConditionVariable(&_cv);
    }
    void notify_all()
    {
        WakeAllConditionVariable(&_cv);
    }

    srwcondition_variable() = default;
    srwcondition_variable(const srwcondition_variable&) = delete;
    srwcondition_variable& operator=(const srwcondition_variable&) = delete;
    srwcondition_variable(srwcondition_variable&&) = delete;
    srwcondition_variable& operator=(srwcondition_variable&&) = delete;
};



class bad_srfuture : public std::runtime_error
{
public:
    bad_srfuture(const char* msg) : std::runtime_error(msg) {}
};

inline void throw_bad_srfuture(bool isValid)
{
    if (!isValid)
    {
        throw bad_srfuture("no state");
    }
}

#ifdef _DEBUG
#ifndef FUTURE_THROW_ON_FALSE
#define FUTURE_THROW_ON_FALSE(stmt) throw_bad_srfuture(stmt);
#endif
#else
#ifndef FUTURE_THROW_ON_FALSE
#define FUTURE_THROW_ON_FALSE(stmt) __noop
#endif
#endif


enum class srfuture_status
{
    deffered, ready, timeout
};


namespace private_details
{
    template <typename T>
    class future_shared_state
    {
    public:
        void wait() const
        {
            // wait until there is either state or error
            unique_srwlock<srwlock> lk(_cs);
            while (!_state && !_error)
            {
                _available.wait(lk);
            }
        }
        srfuture_status wait_for(const std::chrono::milliseconds& timeout_duration)
        {
            // wait until there is either state or error
            unique_srwlock<srwlock> lk(_cs);
            while (!_state && !_error)
            {
                auto cv_status = _available.wait_for(lk, timeout_duration);
                if (cv_status == srcv_status::timeout)
                {
                    return srfuture_status::timeout;
                }
            }
            return srfuture_status::ready;
        }
        T& get()
        {
            if (_state) return *_state;
            if (_error) std::rethrow_exception(_error);
            throw std::logic_error("no state nor error after wait");
        }
        template <typename U>
        void set_value(U&& value)
        {
            unique_srwlock<srwlock> lk(_cs);
            if (_state.has_value() || _error != nullptr)
            {
                throw bad_srfuture("shared state already set");
            }

            _state.emplace(std::forward<U>(value));
            _available.notify_all();
        }

        void set_exception(std::exception_ptr e) 
        {
            unique_srwlock<srwlock> lk(_cs);
            if (_state.has_value() || _error != nullptr)
            {
                throw bad_srfuture("shared state already set");
            }
            _error = e;
            _available.notify_all();
        }
    private:
        mutable srwlock _cs; // _state protection
        mutable srwcondition_variable _available;
        std::optional<T> _state;
        std::exception_ptr _error;
    };
} // namespace private_details
template <typename T> class srpromise;

template <typename T>
class srfuture {
public:
    srfuture() noexcept = default;
    ~srfuture() = default;
    srfuture(srfuture const& other) = delete;
    srfuture& operator=(srfuture const& other) = delete;
    srfuture& operator=(srfuture&& other) noexcept = default;
    srfuture(srfuture&&) noexcept = default;

    T get() 
    {
        // get is assumed to be called from a single thread.

        // step 1: pass the _shared_state to the current thread waiter (invalidate)
        // (other threads calling get() will result in undefined behaviour as _shared_state will be nullptr
        auto shared_state = std::move(_shared_state);

        FUTURE_THROW_ON_FALSE(shared_state != nullptr);

        // step 2:  safely wait for the shared state to fulfill
        shared_state->wait();

        // shared state is fulfilled and no exception is set.
        // step 3: move / copy the state:
        return std::move(shared_state->get()); // https://stackoverflow.com/questions/14856344/when-should-stdmove-be-used-on-a-function-return-value   
    }

    bool valid() const noexcept 
    { 
        return _shared_state != nullptr; 
    }

    void wait() const 
    {
        // The behavior is undefined if valid() == false before the call to this function.
        FUTURE_THROW_ON_FALSE(valid());
        _shared_state->wait();
    }

    srfuture_status wait_for(const std::chrono::milliseconds& timeout_duration) const
    {
        FUTURE_THROW_ON_FALSE(valid());
        _shared_state->wait_for(timeout_duration);
    }
private:
    std::shared_ptr<private_details::future_shared_state<T>> _shared_state = nullptr;

    friend class srpromise<T>;
    srfuture(const std::shared_ptr<private_details::future_shared_state<T>>& shared_state) : _shared_state(shared_state) {}
    srfuture(std::shared_ptr<private_details::future_shared_state<T>>&& shared_state) : _shared_state(std::move(shared_state)) {}
};

template <typename T>
class srpromise 
{
public:
    srpromise() : _shared_state(std::make_shared<private_details::future_shared_state<T>>()) {}
    srpromise(srpromise&& other) noexcept = default;
    srpromise(const srpromise& other) = delete;
    srpromise& operator=(srpromise&& other) noexcept = default;
    srpromise& operator=(srpromise const& rhs) = delete;
    ~srpromise() = default;

    void swap(srpromise& other) noexcept
    { 
        _shared_state.swap(other._shared_state);
    }

    srfuture<T> get_future() 
    {
        return { _shared_state };
    }

    void set_value(const T& value)
    {
        _shared_state->set_value(value);
    }

    void set_value(T&& value) 
    {
        _shared_state->set_value(std::move(value));         
    }

    void set_exception(std::exception_ptr p) 
    {
        _shared_state->set_exception(std::move(p));
    }
private:
    std::shared_ptr<private_details::future_shared_state<T>> _shared_state = nullptr;
};