我正在使用boost::asio(用于套接字),boost::thread(用于线程),libconfig++(用于配置文件读取)和protocol buffers编写多线程服务器(用于协议实现)。
服务器遵循或多或少的路线:main() - >创建一个Application对象 - >运行应用程序对象应用程序加载配置文件,然后创建服务器对象(将配置类作为const传递)。服务器对象自行配置并绑定端口,开始接受,等等。每当检测到新客户端时,服务器都会创建一个新的Client对象,然后创建一个运行客户端连接处理程序的线程。
所有这些都是为了解释配置文件是从我的Application类加载的,然后一直传递到我的Client类。如果libconfig对象一直直接传递给Client,这不应该造成任何麻烦,但众所周知,多线程意味着当两个或多个线程同时访问时内存会损坏。
The way to solve this was discussed in other post最终实现了一个自动解决互斥问题的包装器。
app_config.h
#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_ 1
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/noncopyable.hpp>
#include <libconfig.h++>
#include <string>
namespace BBCP {
namespace App {
class ConfigLock;
class Config {
public:
friend class BBCP::App::ConfigLock;
Config(std::string const &file) :
cfg(new libconfig::Config()),
mutex(new boost::mutex())
{
cfg->readFile(file.c_str());
}
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
};
class Server;
class Client;
class ConfigLock : boost::noncopyable {
public:
ConfigLock(BBCP::App::Config const &wrapper) :
cfg(wrapper.cfg),
mutex(wrapper.mutex),
lock(new LockType(*mutex))
{ }
libconfig::Config &get() throw() { return *cfg; };
private:
boost::shared_ptr<libconfig::Config> cfg;
boost::shared_ptr<boost::mutex> mutex;
typedef boost::lock_guard<boost::mutex> LockType;
boost::shared_ptr<LockType> lock;
};
}
}
#endif
对于懒惰的人来说,这个班级包含......好吧,两个班级(具有讽刺意味的?):BBCP::App::Config
和BBCP::App::ConfigLock
。 BBCP::App::Config
只是加载文件,而BBCP::App::ConfigLock
将BBCP::App::Config
作为参数,然后锁定BBCP::App::Config
的互斥锁。一旦创建了锁,就会调用BBCP::App::ConfigLock::get
,它会返回对libconfig::Config
对象的引用!。
好:
server.cpp:
void BBCP::App::Server::startAccept() {
newClient.reset(new BBCP::App::Client(io_service, config_wrapper));
acceptor.async_accept(newClient->getSocket(), boost::bind(&BBCP::App::Server::acceptHandler, this, boost::asio::placeholders::error));
}
此函数创建一个新的客户端对象,加载了boost::asio::io_service
对象和BBCP::App::Config
对象。
server.cpp
void BBCP::App::Server::acceptHandler(boost::system::error_code const &e) {
if (!acceptor.is_open()) {
// ARR ERROR!
return;
}
if (!e) {
client_pool.create_thread(*newClient);
}
else {
// HANDLE ME ERROR
throw;
}
startAccept();
}
此函数在......好的情况下创建一个新线程或(尚未实现)错误,然后再次启动接受循环。
在此部分之前,客户端代码通常无关紧要:
client.cpp:
void BBCP::App::Client::parseBody() {
BBCP::Protocol::Header header;
BBCP::Protocol::Hello hello;
boost::scoped_ptr<BBCP::App::ConfigLock> lock;
libconfig::Config *cfg;
(...)
switch ((enum BBCP::Protocol::PacketType)header.type()) {
case BBCP::Protocol::HELLO:
(...)
// config_wrapper is a private variable in the client class!
lock.reset(new BBCP::App::ConfigLock(config_wrapper));
// ARRRRRRR HERE BE DRAGOONS!!
*cfg = lock->get();
(...)
lock.reset();
break;
(...)
}
(...)
}
嗯,说实话,我没想到会出现这种错误:
/usr/include/libconfig.h++: In member function ‘void BBCP::App::Client::parseBody()’:
/usr/include/libconfig.h++:338:13: error: ‘libconfig::Config& libconfig::Config::operator=(const libconfig::Config&)’ is private
client.cpp:64:30: error: within this context
client.cpp:71:21: error: request for member ‘exists’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
client.cpp:77:51: error: request for member ‘lookup’ in ‘cfg’, which is of non-class type ‘libconfig::Config*’
但是在这里,我需要一些方法来解决它:(。我已经尝试将BBCP::App::Client
作为BBCP::App::ConfigLock
的朋友类,但后来就像:
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:15:53: error: ‘BBCP::App::Config’ has not been declared
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h:32:5: error: ‘Config’ in namespace ‘BBCP::App’ does not name a type
In file included from ../include/app_config.h:4:0,
from ../include/app_main.h:6,
from main.cpp:18:
../include/app_client.h: In constructor ‘BBCP::App::Client::Client(boost::asio::io_service&, const int&)’:
../include/app_client.h:15:120: error: class ‘BBCP::App::Client’ does not have any field named ‘config_wrapper’
然后我就像O_O一样,所以我放弃了来到这里,再次寻找一些überC++大师hackz0r的帮助,并因为试图访问另一个班级的私人成员而犯下这样的错误。
答案 0 :(得分:1)
首先要弄清楚你是否朝着正确的方向前进,下一步就是到达那里。
为什么Config类型的赋值运算符是私有的?默认情况下,编译器生成的赋值运算符是公共的,因此如果它已被声明为私有,则可能是因为没有复制对象的原因,或者您应该将其公开并且问题不再是问题
在添加好友声明之后,您的特定问题似乎表明您已经错过了包含声明/定义Config类型的标题。然后在代码中有一些错误(一个尚未定义的成员 - 上一个错误的结果?),或者在原始代码中尝试访问指针引用的对象而不解除引用它...
答案 1 :(得分:0)
您可能希望在cfg
中存储指向配置对象的指针,而不是创建副本(并取消引用未初始化的指针):
cfg = &local->get();