C++;结合同步和异步操作的API 我设计了一个C++ API,它既公开同步操作又暴露异步操作。所有操作都可能失败,必须报告失败。异步操作必须提供在完成时执行延续的方法。我正试图以最具可读性和一致性的方式设计API

C++;结合同步和异步操作的API 我设计了一个C++ API,它既公开同步操作又暴露异步操作。所有操作都可能失败,必须报告失败。异步操作必须提供在完成时执行延续的方法。我正试图以最具可读性和一致性的方式设计API,c++,asynchronous,C++,Asynchronous,这是一个示例,说明了我目前的设计: #include <memory> #include <future> using namespace std; class Error { public: Error(int c, string desc) : code(c), description(desc) {} int code; string description; }; template<typename T> class

这是一个示例,说明了我目前的设计:

#include <memory>
#include <future>

using namespace std;

class Error {
public:
    Error(int c, string desc) : code(c), description(desc) {} 

    int code;
    string description;
};

template<typename T>
class Callback {
public:
    virtual void completed(const T& result, unique_ptr<Error> error) = 0;
};

template<typename T>
class PrintCallback : public Callback<T> {
public:
    void completed(const T& result, unique_ptr<Error> error) override {
        if (nullptr != error) {
            printf("An error has occured. Code: %d Description: %s\n", 
                    error->code, error->description.c_str());
        } else {
            printf("Operation completed successfully. Result: %s\n", 
                    to_string(result).c_str());
        }
    }
};

class API {
public:
    void asyncOperation(shared_ptr<Callback<int>> callback) {
        thread([callback]() {
            callback->completed(5, nullptr);
        }).detach();
    }

    int syncOperation(unique_ptr<Error>& error) {
        return 5;
    }

    void asyncFailedOperation(shared_ptr<Callback<int>> callback) {
        thread([callback]() {
            callback->completed(-1, unique_ptr<Error>(new Error(222, "Async Error")));
        }).detach();
    }

    int syncFailedOperation(unique_ptr<Error>& error) {
        error = unique_ptr<Error>(new Error(111, "Sync Error"));
        return -1;
    }
};
#包括
#包括
使用名称空间std;
类错误{
公众:
错误(int c,string desc):代码(c),描述(desc){}
int代码;
字符串描述;
};
模板
类回调{
公众:
虚拟无效已完成(常量T和结果,唯一错误)=0;
};
模板
类PrintCallback:公共回调{
公众:
无效已完成(常量T和结果,唯一错误)覆盖{
if(nullptr!=错误){
printf(“发生错误。代码:%d说明:%s\n”,
错误->代码,错误->说明.c_str());
}否则{
printf(“操作已成功完成。结果:%s\n”,
to_string(result.c_str());
}
}
};
类API{
公众:
无效异步操作(共享\u ptr回调){
线程([回调](){
回调->完成(5,空ptr);
}).detach();
}
int同步操作(唯一\u ptr&错误){
返回5;
}
无效异步失败操作(共享\u ptr回调){
线程([回调](){
回调->已完成(-1,唯一_ptr(新错误(222,“异步错误”));
}).detach();
}
int syncFailedOperation(唯一\u ptr&错误){
错误=唯一(新错误(111,“同步错误”);
返回-1;
}
};
我不喜欢在同步操作中使用error out参数,也不喜欢同步签名和异步签名之间的不一致。 我在讨论两种选择:

  • 将同步操作视为异步操作,并让它们接受回调以返回结果/失败。这种方法在同步和异步操作中更加一致,看起来更干净。另一方面,让一个简单的同步操作与回调一起工作感觉有点奇怪
  • 使用
    std::promise
    std::future
    ,并使用异常报告故障。对于异步操作,将返回一个
    std::future
    ,如果失败,将抛出它的
    get()
    。如果出现故障,同步操作将简单地抛出。这种方法感觉更干净,因为错误处理不会干扰方法签名,异常是C++中错误处理的标准方式。但是,为了得到结果,我必须调用
    future::get()
    ,因此如果我不想阻塞,那么我必须启动另一个线程来等待结果。异步操作的继续在实际设置
    std::promise
    结果的线程上运行也可能很重要。这种方法是这个问题的公认答案-
  • 我想知道:

  • 如果可以避免备选方案2中的额外螺纹
  • 如果备选方案2的弊大于利(尤其是额外的线程)
  • 如果还有其他方法我没有考虑的话
  • 哪种方法被认为是可读性和一致性的最佳选择
  • 谢谢

    • 问:如果替代方案2中的额外线程可以避免
    • 答:避免额外线程的一种方法是将线程池与任务队列一起使用。这种方法仍然使用额外的线程,但是线程的数量在某种程度上是固定的,而不是与曾经创建的任务数量成比例

    • 问:如果备选方案2的弊大于利(特别是额外的线程)

    • 我不这么认为

    • 问:如果有其他方法我没有考虑过的话

    • A:是的。在我看来,最好的方法是使所有内容都异步,提供类似于boost::asio的API,利用boost::asio::io_服务和lambda函数,并实现线程池和任务队列。您可以将sync实现为异步上的包装器,如下所示:进行异步调用并等待std::condition\u变量。异步调用的回调会发出该条件变量的信号

    • 问:哪种方法被认为是可读性和一致性的最佳选择

    • 答:异步代码的可读性不如同步代码。如果可读性对您来说真的很重要,那么就放弃异步。另一方面,如果您想要异步和一致性,请按照我上面概述的那样使所有内容都异步

    除此之外,我认为您不应该实现自己的错误类。请看一下std::error\ U代码(以及error\ U条件、error\ U类别)。一篇好文章很重要。有人已经帮你弄明白了

    目前,这个问题对于堆栈溢出来说太广泛了。你可以写一整本关于API设计的书然而,我有一个挑剔的地方:“异常是C++中进行错误处理的标准方法”——不,它们不是。根据上下文的不同,错误代码或断言通常更清晰。该网站更适合于关于改进现有工作代码的问题。我建议你在咨询他们的意见后,在那里标记你的迁移问题。@ChristianHackl我觉得这应该是一个非常集中的设计问题。我试图在错误处理方面提出异步方法和同步方法之间的一致性问题,我在研究如何解决这个问题(不同的备选方案)以及我对它们的关注时得到了这个问题。@TobySpeight所附的代码是有效的,但它只是为了说明回调方法,而不是为了审查。如果它偏离了真正的问题,我可以删除它。谢谢
    std::error\u code
    看起来像是要走的路,而不是我的错误类,以及bl