c++ - 拆分 C++ 模板文件 intp cpp 和 hpp 文件

标签 c++ c++11 boost stl

<分区>

我从 WebServer 中获取了基础程序

我正在尝试将程序拆分为 Cpp 和 hpp 文件。

server.hpp

#include <boost/asio.hpp>
#include <boost/regex.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#include <unordered_map>
#include <thread>
#include <functional>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <fstream>
#include <vector>

#define CS_WEBSERVER_PORT 0xc080

namespace WebServer {

    template < class socket_type > class WebServerBase 
    {
        public:
            virtual ~ WebServerBase();
            class Response:public std::ostream 
        {
            friend class WebServerBase < socket_type >;
            boost::asio::streambuf streambuf;
            std::shared_ptr < socket_type > socket;
            Response(std::shared_ptr < socket_type > socket):std::ostream(&streambuf), socket(socket);
            public:
            size_t size() {
                return streambuf.size();
            }
        };
            class Content:public std::istream 
        {
            friend class WebServerBase < socket_type >;

            public:
            size_t size() {
                return streambuf.size();
            } 
            const std::string string() const 
            {
                std::stringstream ss;
                ss << rdbuf();
                return ss.str();
            }
            private:
            boost::asio::streambuf & streambuf;
            Content(boost::asio::streambuf & streambuf):std::istream(&streambuf), streambuf(streambuf);
        };

            class Request 
            {
                friend class WebServerBase < socket_type >;

                class iequal_to {
                    public:
                        bool operator() (const std::string & key1, const std::string & key2)const 
                        {
                            return boost::algorithm::iequals(key1, key2);
                        }
                };
                class ihash {
                    public:
                        size_t operator() (const std::string & key)const 
                        {
                            std::size_t seed = 0;
                            for (auto & c:key)
                                boost::hash_combine(seed, std::tolower(c));
                            return seed;
                        }
                };
                public:
                std::string method, path, http_version;
                Content content;
                std::unordered_multimap < std::string, std::string, ihash, iequal_to > header;
                boost::smatch path_match;
                std::string remote_endpoint_address;
                unsigned short remote_endpoint_port;

                private:
                Request():content(streambuf); 
                boost::asio::streambuf streambuf;

                void read_remote_endpoint_data(socket_type & socket);
            };

            class Config 
            {
                friend class WebServerBase < socket_type >;

                Config(unsigned short port, size_t num_threads):num_threads(num_threads), port(port), reuse_address(true);
                size_t num_threads;
                public:
                unsigned short port;
                /*
                 * IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation.
                 * If empty, the address will be any address.
                 */
                std::string address;
                /*Set to false to avoid binding the socket to an address that is already in use.*/
                bool reuse_address;
            };
            ///Set before calling start().
            Config config;

            std::unordered_map < std::string, std::unordered_map < std::string,
                std::function < void (std::shared_ptr < typename WebServerBase < socket_type >::Response >,
                        std::shared_ptr < typename WebServerBase < socket_type >::Request >) > >>resource;

            std::unordered_map < std::string,
                std::function < void (std::shared_ptr < typename WebServerBase < socket_type >::Response >,
                        std::shared_ptr < typename WebServerBase < socket_type >::Request >) > >default_resource;

        private:
            std::vector < std::pair < std::string, std::vector < std::pair < boost::regex,
                std::function < void (std::shared_ptr < typename WebServerBase < socket_type >::Response >,
                        std::shared_ptr < typename WebServerBase < socket_type >::Request >) > >>>>opt_resource;

        public:
            void start();

            void stop();

            ///Use this function if you need to recursively send parts of a longer message
            void send(std::shared_ptr < Response > response,
                    const std::function < void (const boost::system::error_code &) > &callback = nullptr) const;

        protected:
            boost::asio::io_service io_service;
            boost::asio::ip::tcp::acceptor acceptor;
            std::vector < std::thread > threads;

            long timeout_request;
            long timeout_content;

            WebServerBase(unsigned short port, size_t num_threads, long timeout_request,
                    long timeout_send_or_receive):config(port, num_threads), acceptor(io_service),
            timeout_request(timeout_request), timeout_content(timeout_send_or_receive)
        {

        }

            virtual void accept() = 0;

            std::shared_ptr < boost::asio::deadline_timer > set_timeout_on_socket(std::shared_ptr < socket_type > socket,
                    long seconds);

            void read_request_and_content(std::shared_ptr < socket_type > socket);

            bool parse_request(std::shared_ptr < Request > request, std::istream & stream) const;

            void find_resource(std::shared_ptr < socket_type > socket, std::shared_ptr < Request > request);

            void write_response(std::shared_ptr < socket_type > socket, std::shared_ptr < Request > request,
                    std::function < void (std::shared_ptr < typename WebServerBase < socket_type >::Response >,
                        std::shared_ptr < typename WebServerBase < socket_type >::Request >) >
                    &resource_function); 

            template < class socket_type > class Server:public WebServerBase < socket_type > {
            };

            typedef boost::asio::ip::tcp::socket HTTP;

            template <> class Server < HTTP >:public WebServerBase < HTTP > 
            {
                public:
                    Server(unsigned short port, size_t num_threads = 1, long timeout_request = 5, long timeout_content = 300):
                        WebServerBase < HTTP >::WebServerBase(port, num_threads, timeout_request, timeout_content) 
                {

                }

                private:
                    void accept(); 
            };
    }
}

server.cpp

#define WEBSERVER_PORT 0x8080

namespace WebServer {

void WebServerBase::Request::read_remote_endpoint_data(socket_type & socket) 
{
    try {
        remote_endpoint_address = socket.lowest_layer().remote_endpoint().address().to_string();
        remote_endpoint_port = socket.lowest_layer().remote_endpoint().port();
    }
    catch(const std::exception &) 
    {

    }
}

void WebServerBase::start() 
{
    /*Copy the resources to opt_resource for more efficient request processing*/
    opt_resource.clear();
    for (auto & res:resource) 
    {
        for (auto & res_method:res.second) 
        {
            auto it = opt_resource.end();
            for (auto opt_it = opt_resource.begin(); opt_it != opt_resource.end(); opt_it++) 
            {
                if (res_method.first == opt_it->first) 
                {
                    it = opt_it;
                    break;
                }
            }
            if (it == opt_resource.end()) 
            {
                opt_resource.emplace_back();
                it = opt_resource.begin() + (opt_resource.size() - 1);
                it->first = res_method.first;
            }
            it->second.emplace_back(boost::regex(res.first), res_method.second);
        }
    }

    if (io_service.stopped())
        io_service.reset();

    boost::asio::ip::tcp::endpoint endpoint;
    if (config.address.size() > 0)
        endpoint =
            boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(config.address), config.port);
    else
        endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.port);
    acceptor.open(endpoint.protocol());
    acceptor.set_option(boost::asio::socket_base::reuse_address(config.reuse_address));
    acceptor.bind(endpoint);
    acceptor.listen();

    accept();

    //If num_threads>1, start m_io_service.run() in (num_threads-1) threads for thread-pooling
    threads.clear();
    for (size_t c = 1; c < config.num_threads; c++) 
    {
        threads.emplace_back([this] () {
                io_service.run();});
    }

    //Main thread
    io_service.run();

    //Wait for the rest of the threads, if any, to finish as well
    for (auto & t:threads) {
        t.join();
    }
}

void WebServerBase::stop() {
    acceptor.close();
    io_service.stop();
}

///Use this function if you need to recursively send parts of a longer message
void WebServerBase::send(std::shared_ptr < Response > response,
        const std::function < void (const boost::system::error_code &) > &callback = nullptr) const 
{
    boost::asio::async_write(*response->socket, response->streambuf,
            [this, response, callback] (const boost::system::error_code & ec,
                size_t /*bytes_transferred */ ) 
            {
            if (callback)
            callback(ec);
            }) ;
}


std::shared_ptr < boost::asio::deadline_timer > WebServerBase::set_timeout_on_socket(std::shared_ptr < socket_type > socket,
        long seconds) 
{
    std::shared_ptr < boost::asio::deadline_timer > timer(new boost::asio::deadline_timer(io_service));
    timer->expires_from_now(boost::posix_time::seconds(seconds));
    timer->async_wait([socket] (const boost::system::error_code & ec) 
            {
            if (!ec) 
            {
            boost::system::error_code ec;
            socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
            socket->lowest_layer().close();}
            }) ;
    return timer;
}

void WebServerBase::read_request_and_content(std::shared_ptr < socket_type > socket) 
{
    //Create new streambuf (Request::streambuf) for async_read_until()
    //shared_ptr is used to pass temporary objects to the asynchronous functions
    std::shared_ptr < Request > request(new Request());
    request->read_remote_endpoint_data(*socket);

    //Set timeout on the following boost::asio::async-read or write function
    std::shared_ptr < boost::asio::deadline_timer > timer;
    if (timeout_request > 0)
        timer = set_timeout_on_socket(socket, timeout_request);

    boost::asio::async_read_until(*socket, request->streambuf, "\r\n\r\n",
            [this, socket, request, timer] (const boost::system::error_code & ec,
                size_t bytes_transferred) 
            {
            if (timeout_request > 0)
            timer->cancel(); 
            if (!ec) 
            {
            /**
             * request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
             * "After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter"
             * The chosen solution is to extract lines from the stream directly when parsing the header. What is left of the
             * streambuf (maybe some bytes of the content) is appended to in the async_read-function below (for retrieving content).
             */
            size_t num_additional_bytes = request->streambuf.size() - bytes_transferred;
            if (!parse_request(request, request->content))
            return;
            //If content, read that as well
            auto it = request->header.find("Content-Length");
            if (it != request->header.end()) 
            {
                //Set timeout on the following boost::asio::async-read or write function
                std::shared_ptr < boost::asio::deadline_timer > timer;
                if (timeout_content > 0)
                    timer = set_timeout_on_socket(socket, timeout_content);
                unsigned long long content_length; 
                try {
                    content_length = stoull(it->second);
                }
                catch(const std::exception &) 
                {
                    return;
                }
                if (content_length > num_additional_bytes) 
                {
                    boost::asio::async_read(*socket, request->streambuf,
                            boost::asio::transfer_exactly(content_length -
                                num_additional_bytes),
                            [this, socket, request, timer]
                            (const boost::system::error_code & ec,
                             size_t /*bytes_transferred */ ) 
                            {
                            if (timeout_content > 0)
                            timer->cancel(); 
                            if (!ec)
                            find_resource(socket, request);
                            });
                }
                else 
                {
                    if (timeout_content > 0)
                        timer->cancel(); 
                    find_resource(socket, request);
                }
            }
            else 
            {
                find_resource(socket, request);}
            }
            }) ;
}

bool WebServerBase::parse_request(std::shared_ptr < Request > request, std::istream & stream) const 
{
    std::string line;
    getline(stream, line);
    size_t method_end;
    if ((method_end = line.find(' ')) != std::string::npos) 
    {
        size_t path_end;
        if ((path_end = line.find(' ', method_end + 1)) != std::string::npos) 
        {
            request->method = line.substr(0, method_end);
            request->path = line.substr(method_end + 1, path_end - method_end - 1);

            size_t protocol_end;
            if ((protocol_end = line.find('/', path_end + 1)) != std::string::npos) 
            {
                if (line.substr(path_end + 1, protocol_end - path_end - 1) != "HTTP")
                    return false;
                request->http_version = line.substr(protocol_end + 1, line.size() - protocol_end - 2);
            } 
            else
                return false;

            getline(stream, line);
            size_t param_end;
            while ((param_end = line.find(':')) != std::string::npos) 
            {
                size_t value_start = param_end + 1;
                if ((value_start) < line.size()) 
                {
                    if (line[value_start] == ' ')
                        value_start++;
                    if (value_start < line.size())
                        request->header.insert(std::make_pair(line.substr(0, param_end),
                                    line.substr(value_start, line.size() - value_start - 1)));
                }

                getline(stream, line);
            }
        } else
            return false;
    } else
        return false;
    return true;
}

void WebServerBase::find_resource(std::shared_ptr < socket_type > socket, std::shared_ptr < Request > request) 
{
    //Find path- and method-match, and call write_response
    for (auto & res:opt_resource) 
    {
        if (request->method == res.first) 
        {
            for (auto & res_path:res.second) 
            {
                boost::smatch sm_res;
                if (boost::regex_match(request->path, sm_res, res_path.first)) 
                {
                    request->path_match = std::move(sm_res);
                    write_response(socket, request, res_path.second);
                    return;
                }
            }
        }
    }
    auto it_method = default_resource.find(request->method);
    if (it_method != default_resource.end()) 
    {
        write_response(socket, request, it_method->second);
    }
}

void WebServerBase::write_response(std::shared_ptr < socket_type > socket, std::shared_ptr < Request > request,
        std::function < void (std::shared_ptr < typename WebServerBase < socket_type >::Response >,
            std::shared_ptr < typename WebServerBase < socket_type >::Request >) >
        &resource_function) 
{
    //Set timeout on the following boost::asio::async-read or write function
    std::shared_ptr < boost::asio::deadline_timer > timer;
    if (timeout_content > 0)
        timer = set_timeout_on_socket(socket, timeout_content);

    auto response = std::shared_ptr < Response > (new Response(socket),[this, request, timer] (Response * response_ptr) 
            {
            auto response = std::shared_ptr < Response > (response_ptr);
            send(response,[this, response, request,timer] (const boost::system::error_code & ec) 
                    {
                    if (!ec) 
                    {
                    if (timeout_content > 0)
                    timer->cancel(); float http_version; 
                    try {
                    http_version = stof(request->http_version);}
                    catch(const std::exception &) 
                    {
                    return;
                    }

                    auto range = request->header.equal_range("Connection");
                    for (auto it = range.first; it != range.second; it++) 
                    {
                    if (boost::iequals(it->second, "close"))
                    return;
                    }
                    if (http_version > 1.05)
                    read_request_and_content(response->socket);
                    }
                    }
            );}
    );

    try {
        resource_function(response, request);
    }
    catch(const std::exception &) 
    {
        return;
    }
}

    template < class socket_type > class Server:public WebServerBase < socket_type > {
    };

    typedef boost::asio::ip::tcp::socket HTTP;

    template <> class Server < HTTP >:public WebServerBase < HTTP > 
    {
        void Server::accept() 
        {
            //Create new socket for this connection
            //Shared_ptr is used to pass temporary objects to the asynchronous functions
            std::shared_ptr < HTTP > socket(new HTTP(io_service));

            acceptor.async_accept(*socket,[this, socket] (const boost::system::error_code & ec) 
                    {
                    //Immediately start accepting a new connection
                    accept(); if (!ec) {
                    boost::asio::ip::tcp::no_delay option(true);
                    socket->set_option(option); read_request_and_content(socket);}
                    }) ;
        }
    };
}

typedef WebServer::Server < WebServer::HTTP > HttpServer;
void default_resource_send(const HttpServer &server, std::shared_ptr<HttpServer::Response> response,
        std::shared_ptr<std::ifstream> ifs, std::shared_ptr<std::vector<char> > buffer);
int main()
{
    //HTTP-server at port c080 using 1 thread
    HttpServer server(WEBSERVER_PORT, 1);

    server.resource["^/match/([0-9]+)$"]["GET"] =
        [&server] (std::shared_ptr < HttpServer::Response > response, std::shared_ptr < HttpServer::Request > request) {
            std::string number = request->path_match[1];
            *response << "HTTP/1.1 200 OK\r\nContent-Length: " << number.length() << "\r\n\r\n" << number;
        };

    using namespace boost::property_tree;
    server.default_resource["GET"]=
        [&server](std::shared_ptr<HttpServer::Response> response, std::shared_ptr<HttpServer::Request> request) 
        {
            const auto web_root_path=boost::filesystem::canonical("web");
            boost::filesystem::path path=web_root_path;
            path/=request->path;
            if(boost::filesystem::exists(path)) 
            {
                path=boost::filesystem::canonical(path);
                //Check if path is within web_root_path
                if(std::distance(web_root_path.begin(), web_root_path.end())<=std::distance(path.begin(), path.end()) &&
                        std::equal(web_root_path.begin(), web_root_path.end(), path.begin())) 
                {
                    if(boost::filesystem::is_directory(path))
                        path/="index.html";
                    if(boost::filesystem::exists(path) && boost::filesystem::is_regular_file(path)) 
                    {
                        auto ifs=std::make_shared<std::ifstream>();
                        ifs->open(path.string(), std::ifstream::in | std::ios::binary);

                        if(*ifs) 
                        {
                            //read and send 128 KB at a time
                            std::streamsize buffer_size=131072;
                            auto buffer=std::make_shared<std::vector<char> >(buffer_size);

                            ifs->seekg(0, std::ios::end);
                            auto length=ifs->tellg();

                            ifs->seekg(0, std::ios::beg);

                            *response << "HTTP/1.1 200 OK\r\nContent-Length: " << length << "\r\n\r\n";
                            default_resource_send(server, response, ifs, buffer);
                            return;
                        }
                    }
                }
            }
            std::string content="Could not open path "+request->path;
            *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content;
        };

    std::thread server_thread([&server] ()
            {
            //Start server
            server.start();
            });

    server_thread.join();

    return 0;
}

void default_resource_send(const HttpServer &server, std::shared_ptr<HttpServer::Response> response,
        std::shared_ptr<std::ifstream> ifs, std::shared_ptr<std::vector<char> > buffer) 
{
    std::streamsize read_length;
    if((read_length=ifs->read(&(*buffer)[0], buffer->size()).gcount())>0) 
    {
        response->write(&(*buffer)[0], read_length);
        if(read_length==static_cast<std::streamsize>(buffer->size())) 
        {
            server.send(response, [&server, response, ifs, buffer](const boost::system::error_code &ec) 
                    {
                    if(!ec)
                    default_resource_send(server, response, ifs, buffer);
                    else
                    std::cerr << "Connection interrupted" << std::endl;
                    });
        }
    }
}

我编译如下。

g++ -std=c++11 server.cpp  -lboost_system -lboost_thread -lboost_filesystem -lboost_regex -lpthread -o server

但是我得到以下错误

In file included from httpserver.cpp:18:0:
httpserver.hpp:165:24: error: declaration of ‘class socket_type’
             template < class socket_type > class Server:public WebServerBase < socket_type > {
                        ^
server.hpp:23:16: error:  shadows template parm ‘class socket_type’
     template < class socket_type > class WebServerBase 
                ^
server.hpp:170:23: error: explicit specialization in non-namespace scope ‘class WebServer::WebServerBase<socket_type>’
             template <> class Server < HTTP >:public WebServerBase < HTTP > 
                       ^
server.hpp:170:31: error: template parameters not deducible in partial specialization:
             template <> class Server < HTTP >:public WebServerBase < HTTP > 
                               ^
server.hpp:170:31: note:         ‘socket_type’
server.hpp:182:5: error: expected ‘;’ after class definition
     }
     ^
server.hpp: In constructor ‘WebServer::WebServerBase<socket_type>::Response::Response(std::shared_ptr<_Tp1>)’:
server.hpp:32:101: error: expected ‘{’ at end of input
             Response(std::shared_ptr < socket_type > socket):std::ostream(&streambuf), socket(socket);
                                                                                                     ^
server.hpp: In constructor ‘WebServer::WebServerBase<socket_type>::Content::Content(boost::asio::streambuf&)’:
server.hpp:54:102: error: expected ‘{’ at end of input
             Content(boost::asio::streambuf & streambuf):std::istream(&streambuf), streambuf(streambuf);
                                                                                                      ^
server.hpp: In constructor ‘WebServer::WebServerBase<socket_type>::Request::Request()’:
server.hpp:87:44: error: expected ‘{’ at end of input
                 Request():content(streambuf); 
                                            ^
server.hpp: In constructor ‘WebServer::WebServerBase<socket_type>::Config::Config(short unsigned int, size_t)’:
server.hpp:97:121: error: expected ‘{’ at end of input
                 Config(unsigned short port, size_t num_threads):num_threads(num_threads), port(port), reuse_address(true);
                                                                                                                         ^
server.cpp: At global scope:
server.cpp:24:6: error: ‘template<class socket_type> class WebServer::WebServerBase’ used without template parameters
 void WebServerBase::Request::read_remote_endpoint_data(socket_type & socket) 
      ^
server.cpp:24:56: error: variable or field ‘read_remote_endpoint_data’ declared void
 void WebServerBase::Request::read_remote_endpoint_data(socket_type & socket) 
                                                        ^
server.cpp:24:56: error: ‘socket_type’ was not declared in this scope
server.cpp:24:56: note: suggested alternative:
‘boost::asio::detail::socket_type’
 typedef int socket_type;
             ^
server.cpp:435:1: error: expected ‘}’ at end of input
 }
 ^

我不明白这些错误是什么意思。括号似乎与我的眼睛正确匹配。如果有人能帮助我,那就太好了。

最佳答案

第 165 行:template <class socket_type> class Server:public WebserverBase<socket_type>应该在命名空间 Webserver不在类内 WebserverBase

关于c++ - 拆分 C++ 模板文件 intp cpp 和 hpp 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38342316/

相关文章:

c++ - 存储动态创建对象的列表/vector 的最佳方式是什么?

c++ - Hadoop C++,运行wordcount示例时出错

c++ - 使用 std::complex<double> 为 `*` 操作将 int 转换为 double

c++ - 构造函数和 initializer_list

c++ - 使用 Boost::Regex 获取最后一场比赛

c++ - constexpr 函数允许什么?

c++ - 查找两个数组中最接近的数字

c++ - 应用中的歧义

c++ - 我可以从 boost 的 weak_ptr 获得原始指针吗?

c++ - 启用_if : case of templated method of a template base inherited several times