首页 > 解决方案 > 错误:“message_source&”不是类、结构或联合类型(但是,message_source 是一个类)

问题描述

每个人。我正在尝试编写一些代码来通过参与者模式实现并发系统。演员的界面是这样的:

template <class SourceMessageType, class MessageType>
class actor{
public:
    using value_type = MessageType;
    void process_message(SourceMessageType&& message);
    template <class EmitFunction>
    void on_message(EmitFunction emit);
private:
    std::function<void(MessageType&&)> m_emit;
};

在我的程序中,有两个actor,一个是Service Actor,另一个是Sink Actor。他们的定义如下(我将在这个问题的底部放一个最小的工作示例):

class service {
public:
    using value_type = std::string;
    explicit service(boost::asio::io_service& service,
                     unsigned short port = 42042);
    service(const service&) = delete;
    service(service&& other) = default;

    template <class EmitFunction>
    void on_message(EmitFunction emit) {
        m_emit = emit;
        do_accept();
    }
private:
    void do_accept();
    tcp::acceptor m_acceptor;
    tcp::socket m_socket;
    std::function<void(std::string&&)> m_emit;
};

template <class Sender, class Function,
          class MessageType = typename Sender::value_type>
class sink_impl {
public:
    using value_type = MessageType;
    sink_impl(Sender&& sender, Function function)
        : m_sender(std::move(sender)), m_function(function) {
        m_sender.on_message([this](MessageType&& message) {
            process_message(std::move(message));
        });
    }
    void process_message(MessageType&& message) const {
        std::invoke(m_function, std::move(message));
    }
private:
    Sender m_sender;
    Function m_function;
};

如您所见,Service Actor 的唯一职责是向其他 Actor 发送消息,但它从不接收来自他们的任何消息。Sink Actor 则完全相反。为了测试我的演员,我在下面编写了主要功能并使用 telnet 来模拟客户端。

template <class Function>
struct sink_helper {
    Function function;
};

template <class Function>
auto sink(Function&& function) {
    return detail::sink_helper<Function>{std::forward<Function>(function)};
}

template <class Sender, class Function>
auto operator|(Sender&& sender, detail::sink_helper<Function> sink) {
    return detail::sink_impl<Sender, Function>(std::forward<Sender>(sender),sink.function);
}
int main(){
    boost::asio::io_service event_loop;
    auto sink_to_cerr =
        sink([](const auto& message) { std::cerr << message << '\n'; });
    // Starting the Boost.ASIO service
    auto pipeline = service(event_loop) | sink_to_cerr;
    cerr << "Service is running...\n";
    event_loop.run();
}

到目前为止一切顺利,我的程序可以接收我通过 telnet 发送给它的任何消息。而且你知道,异步操作总是很难自动测试,所以我想在我的程序eariser中添加一个mock_service类来测试。我mock_service的如下:

class message_source{
public:
    message_source(std::initializer_list<std::string>&& inits) : m_source{inits}{}
    message_source(const std::vector<std::string>& other) : m_source(other){}
    message_source(const message_source&) = delete;
    message_source(message_source&& other) : m_source(std::move(other.m_source)){}
    template <class EmitFunction>
    void on_message(EmitFunction emit) {
        m_emit = emit;
    }
private:
    std::function<void(std::string&&)> m_emit;
    std::vector<std::string> m_source;
};
// omit some irrelevant codes
int main(){
    boost::asio::io_service event_loop;
    auto sink_to_cerr =
        sink([](const auto& message) { std::cerr << message << '\n'; });
    // Starting the Boost.ASIO service
    auto pipeline = mock_service(event_loop) | sink_to_cerr;
    cerr << "Service is running...\n";
    event_loop.run();
}

但这一次,我收到了一条编译错误消息:

g++ mwe.cc -std=c++17 -lpthread
mwe.cc: In instantiation of ‘auto operator|(Sender&&, sink_helper<Function>) [with Sender = mock_service&; Function = main(int, const char**)::<lambda(const auto:1&)>]’:
mwe.cc:134:30:   required from here
mwe.cc:123:12: error: ‘mock_service&’ is not a class, struct, or union type
  123 |     return sink_impl<Sender, Function>(std::forward<Sender>(sender),
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  124 |                                           sink.function);
      |                                           ~~~~~~~~~~~~~~
mwe.cc: In function ‘int main(int, const char**)’:
mwe.cc:134:10: error: ‘void pipeline’ has incomplete type
  134 |     auto pipeline = source | sink_to_cerr;

此错误消息似乎没有为我提供任何有用的信息,因为 mock_service 正是类类型。所以我的问题是为什么会发生这个错误以及如何让它编译?


为了您的方便,这里是一个最小的工作示例。

#include <boost/asio.hpp>
#include <functional>
#include <iostream>
using boost::asio::ip::tcp;

template <typename EmitFunction>
class session : public std::enable_shared_from_this<session<EmitFunction>> {
public:
    session(tcp::socket&& socket, EmitFunction emit)
        : m_socket(std::move(socket)), m_emit(emit) {}
    void start() { do_read(); }

private:
    using shared_session = std::enable_shared_from_this<session<EmitFunction>>;
    void do_read() {
        auto self = shared_session::shared_from_this();
        boost::asio::async_read_until(
            m_socket, m_data, '\n',
            [this, self](const boost::system::error_code& error,
                         std::size_t size) {
                if (!error) {
                    std::istream is(&m_data);
                    std::string line;
                    std::getline(is, line);
                    m_emit(std::move(line));
                    do_read();
                }
            });
    }
    tcp::socket m_socket;
    boost::asio::streambuf m_data;
    EmitFunction m_emit;
};

template <class Socket, class EmitFunction>
auto make_shared_session(Socket&& socket, EmitFunction&& emit) {
    return std::make_shared<session<EmitFunction>>(
        std::forward<Socket>(socket), std::forward<EmitFunction>(emit));
}

class service {
public:
    using value_type = std::string;
    explicit service(boost::asio::io_service& service,unsigned short port = 42042)
        :m_acceptor(service, tcp::endpoint(tcp::v4(), port)), m_socket(service) {}
    service(const service&) = delete;
    service(service&& other) = default;

    template <class EmitFunction>
    void on_message(EmitFunction emit) {
        m_emit = emit;
        do_accept();
    }

private:
    void do_accept(){
        m_acceptor.async_accept(
        m_socket, [this](const boost::system::error_code& error) {
            if (!error) {
                make_shared_session(std::move(m_socket), m_emit)->start();
            } else {
                std::cerr << error.message() << '\n';
            }
            do_accept();
        });
    }
    tcp::acceptor m_acceptor;
    tcp::socket m_socket;
    std::function<void(std::string&&)> m_emit;
    friend std::ostream& operator<<(std::ostream& out, const service& service) {
        return out << "service object";
    }
};

class mock_service{
public:
    mock_service(std::initializer_list<std::string>&& inits) : m_source{inits}{}
    mock_service(const std::vector<std::string>& other) : m_source(other){}
    mock_service(const mock_service& other) : m_source(other.m_source){}
    mock_service(mock_service&& other) : m_source(std::move(other.m_source)){}
    template <class EmitFunction>
    void on_message(EmitFunction emit) {
        m_emit = emit;
    }
private:
    std::function<void(std::string&&)> m_emit;
    std::vector<std::string> m_source;
};

template <class Sender, class Function,
          class MessageType = typename Sender::value_type>
class sink_impl {
public:
    using value_type = MessageType;
    sink_impl(Sender&& sender, Function function)
        : m_sender(std::move(sender)), m_function(function) {
        m_sender.on_message([this](MessageType&& message) {
            process_message(std::move(message));
        });
    }

    void process_message(MessageType&& message) const {
        std::invoke(m_function, std::move(message));
    }

private:
    Sender m_sender;
    Function m_function;
};

template <class Function>
struct sink_helper {
    Function function;
};

template <class Function>
auto sink(Function&& function) {
    return sink_helper<Function>{std::forward<Function>(function)};
}

template <class Sender, class Function>
auto operator|(Sender&& sender, sink_helper<Function> sink) {
    return sink_impl<Sender, Function>(std::forward<Sender>(sender),
                                          sink.function);
}

int main(int argc, char const* argv[]) {
    boost::asio::io_service event_loop;
    auto sink_to_cerr =
        sink([](const auto& message) { std::cerr << message << '\n'; });
    auto pipeline = service(event_loop) | sink_to_cerr;
    //auto pipeline = mock_service{"", " ", "\n", "hello world", "hello world\n"} | sink_to_cerr;  //Compile Error
    std::cerr << "Service is running...\n";
    event_loop.run();
}

编译命令是g++ mwe.cc -std=c++17 -lpthread

顺便说一句,我的环境是:

操作系统:Ubuntu-20.04

编译器:GCC 版本 9.3.0

标签: c++unit-testingfunctional-programmingactor

解决方案


推荐阅读