首页 > 解决方案 > 如何在源/标题中拆分下面的示例代码?

问题描述

有没有一种很好的方法将以下示例 C++ 代码拆分为源代码和标头,这样服务器的所有用户都不需要间接包含实际上只需要服务器内部的标头? Daytime.3 的源列表

//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

std::string make_daytime_string()
{
  using namespace std; // For time_t, time and ctime;
  time_t now = time(0);
  return ctime(&now);
}

class tcp_connection
  : public boost::enable_shared_from_this<tcp_connection>
{
public:
  typedef boost::shared_ptr<tcp_connection> pointer;

  static pointer create(boost::asio::io_service& io_service)
  {
    return pointer(new tcp_connection(io_service));
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    message_ = make_daytime_string();

    boost::asio::async_write(socket_, boost::asio::buffer(message_),
        boost::bind(&tcp_connection::handle_write, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

private:
  tcp_connection(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  void handle_write(const boost::system::error_code& /*error*/,
      size_t /*bytes_transferred*/)
  {
  }

  tcp::socket socket_;
  std::string message_;
};

class tcp_server
{
public:
  tcp_server(boost::asio::io_service& io_service)
    : acceptor_(io_service, tcp::endpoint(tcp::v4(), 13))
  {
    start_accept();
  }

private:
  void start_accept()
  {
    tcp_connection::pointer new_connection =
      tcp_connection::create(acceptor_.get_io_service());

    acceptor_.async_accept(new_connection->socket(),
        boost::bind(&tcp_server::handle_accept, this, new_connection,
          boost::asio::placeholders::error));
  }

  void handle_accept(tcp_connection::pointer new_connection,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_connection->start();
    }

    start_accept();
  }

  tcp::acceptor acceptor_;
};

int main()
{
  try
  {
    boost::asio::io_service io_service;
    tcp_server server(io_service);
    io_service.run();
  }
  catch (std::exception& e)
  {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

由于某种原因,我能找到的大多数示例都假设所有代码都在一个文件中。

这个问题可能看起来很愚蠢,但每次我尝试重构它时,一些模板都会崩溃。我真的可以使用这个例子。

标签: c++boostrefactoring

解决方案


对此的一般答案是使用 Pimpl Idiom。您可以使标题尽可能简单

#pragma once
#include <memory>
class tcp_server {
  public:
    tcp_server();
    ~tcp_server();
    void run();

  private:
    struct impl;
    std::unique_ptr<impl> _pimpl;
};

那么main.cpp可以是:

#include "server.h"
#include <iostream>
int main()
{
    try {
        tcp_server server;
        server.run();
    } catch (std::exception const& e) {
        std::cerr << e.what() << std::endl;
    }
}

当然,这留下了您如何实施的问题test.cpp

tcp_server::tcp_server() : _pimpl(std::make_unique<impl>()) {}
void tcp_server::run() { _pimpl->run(); }

并且所有的逻辑都移到了实现类型中,所有的本地函数都可以有文件链接。

现场演示

活在魔杖盒上

  • 文件test.cpp

     #include "server.h"
     #include <iostream>
     int main()
     {
         try {
             tcp_server server;
             server.run();
         } catch (std::exception const& e) {
             std::cerr << e.what() << std::endl;
         }
     }
    
  • 文件server.h

     #pragma once
     #include <memory>
     class tcp_server {
       public:
         tcp_server();
         ~tcp_server();
         void run();
    
       private:
         struct impl;
         std::unique_ptr<impl> _pimpl;
     };
    
  • 文件server.cpp

     #include "server.h"
     #include <boost/asio.hpp>
     #include <boost/bind/bind.hpp>
     #include <boost/enable_shared_from_this.hpp>
     #include <boost/shared_ptr.hpp>
     #include <ctime>
     #include <string>
    
     using boost::asio::ip::tcp;
    
     namespace /*anonymous, file linkage*/ {
         static std::string make_daytime_string()
         {
             using namespace std; // For time_t, time and ctime;
             time_t now = time(0);
             return ctime(&now);
         }
    
         class tcp_connection
             : public boost::enable_shared_from_this<tcp_connection> {
           public:
             typedef boost::shared_ptr<tcp_connection> pointer;
    
             static pointer create(boost::asio::any_io_executor executor)
             {
                 return pointer(new tcp_connection(executor));
             }
    
             tcp::socket& socket() { return socket_; }
    
             void start()
             {
                 message_ = make_daytime_string();
    
                 boost::asio::async_write(
                     socket_, boost::asio::buffer(message_),
                     boost::bind(&tcp_connection::handle_write, shared_from_this(),
                                 boost::asio::placeholders::error,
                                 boost::asio::placeholders::bytes_transferred));
             }
    
           private:
             tcp_connection(boost::asio::any_io_executor executor)
                 : socket_(executor)
             {
             }
    
             void handle_write(const boost::system::error_code& /*error*/,
                               size_t /*bytes_transferred*/)
             {
             }
    
             tcp::socket socket_;
             std::string message_;
         };
     } // namespace
    
     struct tcp_server::impl {
         void run() { io_service_.run(); }
         void start_accept()
         {
             tcp_connection::pointer new_connection =
                 tcp_connection::create(acceptor_.get_executor());
    
             acceptor_.async_accept(new_connection->socket(),
                                    boost::bind(&impl::handle_accept, this,
                                                new_connection,
                                                boost::asio::placeholders::error));
         }
    
         void handle_accept(tcp_connection::pointer          new_connection,
                            const boost::system::error_code& error)
         {
             if (!error) {
                 new_connection->start();
             }
    
             start_accept();
         }
    
         boost::asio::io_service io_service_;
         tcp::acceptor acceptor_{io_service_, tcp::endpoint(tcp::v4(), 13)};
     };
    
     tcp_server::tcp_server() : _pimpl(std::make_unique<impl>()) {}
     tcp_server::~tcp_server() = default;
    
     void tcp_server::run() { _pimpl->run(); }
    

推荐阅读