@@ -12,10 +12,19 @@ | |||
#include <booster/callback.h> | |||
#include <booster/hold_ptr.h> | |||
#include <string> | |||
#include <set> | |||
namespace cppcms { | |||
namespace plugin { | |||
class CPPCMS_API signature_error : public booster::bad_cast { | |||
public: | |||
signature_error(std::string const &msg); | |||
~signature_error() throw(); | |||
virtual char const *what() const throw(); | |||
private: | |||
std::string msg_; | |||
}; | |||
class CPPCMS_API manager { | |||
public: | |||
@@ -31,7 +40,10 @@ public: | |||
typedef refcounted_ptr (*entry_point_type)(); | |||
/// | |||
/// Get plugin entry by plugin name, entry point name and type | |||
/// Get plugin entry by \a plugin_name, \a entry_name and \a Signature | |||
/// | |||
/// If entry is not found or no entry point is created throws cppcms_error,if Signature mismatches the callback type | |||
/// throws booster::bad_cast | |||
/// | |||
/// For example | |||
/// \code | |||
@@ -49,24 +61,23 @@ public: | |||
/// | |||
/// \ver{v1_2} | |||
template<typename Signature> | |||
static | |||
booster::callback<Signature> | |||
entry(std::string const &name) | |||
entry(std::string const &plugin_name,std::string const &entry_name) | |||
{ | |||
typedef booster::callback<Signature> callback_type; | |||
typedef typename callback_type::callable_type callable_type; | |||
typedef typename callback_type::pointer_type pointer_type; | |||
entry_point_type plugin_call = instance().get_entry(name); | |||
entry_point_type plugin_call = instance().get_entry(plugin_name,entry_name); | |||
if(!plugin_call) | |||
throw cppcms_error("Could not find plugin `" + name + "'"); | |||
throw cppcms_error("Could not find entry `" + entry_name + "' in plugin `" + plugin_name + "'"); | |||
refcounted_ptr call = plugin_call(); | |||
if(!call) | |||
throw cppcms_error("Failed to create callback from `" + name + "'"); | |||
throw cppcms_error("Failed to create callback from plugin `"+plugin_name+"':entry `" + entry_name + "'"); | |||
callable_type *real_call = dynamic_cast<callable_type *>(call.get()); | |||
if(!real_call) { | |||
throw booster::bad_cast(); | |||
throw signature_error("Invalid signature request in plugin `"+ plugin_name +"':entry `"+entry_name+"', expected following signaure `" + instance().signature(plugin_name,entry_name) + "'"); | |||
} | |||
pointer_type ptr(real_call); | |||
callback_type result(ptr); | |||
@@ -74,18 +85,51 @@ public: | |||
} | |||
/// | |||
/// Check if plugin entry of type Signature exists in a plugin \a plugin named \a name | |||
/// | |||
template<typename Signature> | |||
bool has_entry(std::string const &plugin,std::string const &name) | |||
{ | |||
typedef booster::callback<Signature> callback_type; | |||
entry_point_type plugin_call = get_entry(plugin,name); | |||
if(!plugin_call) | |||
return false; | |||
return dynamic_cast<callback_type *>(plugin_call().get())!=0; | |||
} | |||
/// | |||
/// Get entry point that creates a base of booster::callback::callable_type | |||
/// | |||
entry_point_type get_entry(std::string const &name); | |||
entry_point_type get_entry(std::string const &plugin,std::string const &name); | |||
/// | |||
/// Get textual representation of entry point signature - for logging purposes, if not found returns empty string | |||
/// | |||
std::string signature(std::string const &plugin,std::string const &name); | |||
/// | |||
/// Addes entry to the plugin manager - thread safe function | |||
/// | |||
void add_entry(char const *name,entry_point_type entry); | |||
void add_entry(char const *plugin_name,char const *entry_name,entry_point_type entry,char const *signature); | |||
/// | |||
/// Removes entry from the plugin manager - thread safe function | |||
/// | |||
void remove_entry(entry_point_type entry); | |||
/// | |||
/// Get list of all plugin names | |||
/// | |||
std::set<std::string> plugins(); | |||
/// | |||
/// Get list of all entry names for \a plugin | |||
/// | |||
std::set<std::string> entries(std::string const &plugin); | |||
/// | |||
/// Returns true if plugin \a name is loaded | |||
/// | |||
bool has_plugin(std::string const &name); | |||
private: | |||
manager(); | |||
~manager(); | |||
@@ -93,12 +137,15 @@ private: | |||
void operator=(manager const &); | |||
struct _data; | |||
struct entry_type; | |||
booster::hold_ptr<_data> d; | |||
}; | |||
#define CPPCMS_NAMED_PLUGIN_ENTRY(type,call,name) \ | |||
#define CPPCMS_PLUGIN_CONCAT(x,y) x ## y | |||
#define CPPCMS_PLUGIN_CONCAT2(x,y) CPPCMS_PLUGIN_CONCAT(x,y) | |||
#define CPPCMS_NAMED_PLUGIN_ENTRY(plugin_name,call_name,call,type,signature) \ | |||
namespace { \ | |||
struct stpg_##__LINE__ { \ | |||
struct CPPCMS_PLUGIN_CONCAT2(stpg_ , __LINE__) { \ | |||
static booster::intrusive_ptr<booster::refcounted> entry() \ | |||
{ \ | |||
typedef booster::callback<type> ct; \ | |||
@@ -107,16 +154,19 @@ namespace { \ | |||
booster::intrusive_ptr<booster::refcounted> ptr(tmp); \ | |||
return ptr; \ | |||
} \ | |||
stpg_##__LINE__() { \ | |||
cppcms::plugin::manager::instance().add_entry(name,&entry); \ | |||
CPPCMS_PLUGIN_CONCAT2(stpg_,__LINE__) () { \ | |||
cppcms::plugin::manager::instance().add_entry( \ | |||
plugin_name,call_name,&entry,signature \ | |||
); \ | |||
\ | |||
} \ | |||
~stpg_##__LINE__() { \ | |||
~CPPCMS_PLUGIN_CONCAT2(stpg_,__LINE__)() { \ | |||
cppcms::plugin::manager::instance().remove_entry(&entry); \ | |||
} \ | |||
} instance_of_stpg_##__LINE__; \ | |||
} CPPCMS_PLUGIN_CONCAT2(instance_of_stpg_,__LINE__); \ | |||
} | |||
#define CPPCMS_PLUGIN_ENTRY(type,call) CPPCMS_NAMED_PLUGIN_ENTRY(type,call,#call) | |||
#define CPPCMS_PLUGIN_ENTRY(name,call,type) CPPCMS_NAMED_PLUGIN_ENTRY(#name,#call,name :: call,type,#type) | |||
} // plugin | |||
@@ -19,8 +19,17 @@ namespace { | |||
} init_inst; | |||
} | |||
struct single_entry { | |||
single_entry(manager::entry_point_type ep=NULL,char const *sig="") : entry(ep),signature(sig) {} | |||
manager::entry_point_type entry; | |||
std::string signature; | |||
}; | |||
typedef std::map<std::string,single_entry> entries_type; | |||
typedef std::map<std::string,entries_type> plugins_type; | |||
struct manager::_data { | |||
std::map<std::string,manager::entry_point_type> mapping; | |||
plugins_type plugins; | |||
booster::mutex lock; | |||
}; | |||
@@ -38,12 +47,11 @@ manager::~manager() | |||
{ | |||
} | |||
void manager::add_entry(char const *name,manager::entry_point_type e) | |||
void manager::add_entry(char const *plugin,char const *name,manager::entry_point_type e,char const *sig) | |||
{ | |||
try { | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
std::string key=name; | |||
d->mapping.insert(std::make_pair(key,e)); | |||
d->plugins[plugin].insert(std::make_pair<std::string,single_entry>(name,single_entry(e,sig))); | |||
} | |||
catch(...){} | |||
} | |||
@@ -51,26 +59,78 @@ void manager::remove_entry(manager::entry_point_type e) | |||
{ | |||
try { | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
for(std::map<std::string,manager::entry_point_type>::iterator p=d->mapping.begin();p!=d->mapping.end();++p) { | |||
if(p->second==e) { | |||
d->mapping.erase(p); | |||
return; | |||
for(plugins_type::iterator p=d->plugins.begin();p!=d->plugins.end();++p) { | |||
for(entries_type::iterator it = p->second.begin();it!=p->second.end();++it) { | |||
if(it->second.entry==e) { | |||
p->second.erase(it); | |||
if(p->second.empty()) | |||
d->plugins.erase(p); | |||
return; | |||
} | |||
} | |||
} | |||
} | |||
catch(...){} | |||
} | |||
manager::entry_point_type manager::get_entry(std::string const &name) | |||
manager::entry_point_type manager::get_entry(std::string const &plugin,std::string const &name) | |||
{ | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
std::map<std::string,manager::entry_point_type>::const_iterator p=d->mapping.find(name); | |||
if(p==d->mapping.end()) | |||
plugins_type::const_iterator p=d->plugins.find(plugin); | |||
if(p==d->plugins.end()) | |||
return 0; | |||
return p->second; | |||
entries_type::const_iterator it=p->second.find(name); | |||
if(it!=p->second.end()) | |||
return it->second.entry; | |||
return 0; | |||
} | |||
std::string manager::signature(std::string const &plugin,std::string const &name) | |||
{ | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
plugins_type::const_iterator p=d->plugins.find(plugin); | |||
if(p==d->plugins.end()) | |||
return ""; | |||
entries_type::const_iterator it=p->second.find(name); | |||
if(it!=p->second.end()) | |||
return it->second.signature; | |||
return ""; | |||
} | |||
std::set<std::string> manager::plugins() | |||
{ | |||
std::set<std::string> r; | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
for(plugins_type::const_iterator p=d->plugins.begin();p!=d->plugins.end();++p) { | |||
r.insert(p->first); | |||
} | |||
return r; | |||
} | |||
bool manager::has_plugin(std::string const &plugin) | |||
{ | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
return d->plugins.find(plugin)!=d->plugins.end(); | |||
} | |||
std::set<std::string> manager::entries(std::string const &name) | |||
{ | |||
std::set<std::string> r; | |||
booster::unique_lock<booster::mutex> guard(d->lock); | |||
plugins_type::const_iterator pit = d->plugins.find(name); | |||
if(pit==d->plugins.end()) | |||
return r; | |||
for(entries_type::const_iterator p=pit->second.begin();p!=pit->second.end();++p) { | |||
r.insert(p->first); | |||
} | |||
return r; | |||
} | |||
signature_error::signature_error(std::string const &msg) : msg_(msg) {} | |||
signature_error::~signature_error() throw() {} | |||
char const *signature_error::what() const throw() | |||
{ | |||
return msg_.c_str(); | |||
} | |||
} // plugin | |||
} // cppcms |
@@ -2,6 +2,7 @@ | |||
#include <booster/shared_object.h> | |||
#include <iostream> | |||
#include "test.h" | |||
#include "plugin_base.h" | |||
int main(int argc,char **argv) | |||
@@ -12,29 +13,44 @@ int main(int argc,char **argv) | |||
} | |||
std::string path = argv[1]; | |||
try { | |||
using cppcms::plugin::manager; | |||
booster::shared_object obj(path + "/" + booster::shared_object::name("plugin")); | |||
{ | |||
std::cout << "- Normal call" << std::endl; | |||
booster::callback<std::string(std::string const &)> cb; | |||
cb = cppcms::plugin::manager::entry<std::string(std::string const &)>("foo::lower"); | |||
cb = manager::instance().entry<std::string(std::string const &)>("foo","lower"); | |||
TEST(cb("Hello World")=="hello world"); | |||
bar_base *b=manager::instance().entry<bar_base *(std::string const &)>("foo","bar::create")("hello"); | |||
TEST(b->msg() == std::string("hello")); | |||
delete b; | |||
try { | |||
std::cout << "- Bad signature" << std::endl; | |||
cppcms::plugin::manager::entry<std::string(std::string &)>("foo::lower"); | |||
manager::instance().entry<std::string(std::string &)>("foo","lower"); | |||
TEST(!"Never Get There"); | |||
} | |||
catch(booster::bad_cast const &) {} | |||
catch(cppcms::plugin::signature_error const &e) { | |||
std::string msg = e.what(); | |||
TEST(msg.find("std::string(std::string const &)")!=std::string::npos); | |||
} | |||
catch(...) { throw std::runtime_error("Something else thrown"); } | |||
std::cout << "- Iteration" << std::endl; | |||
TEST(manager::instance().has_plugin("foo")); | |||
TEST(manager::instance().plugins().size()==1); | |||
TEST(*manager::instance().plugins().begin()=="foo"); | |||
TEST(manager::instance().entries("foo").size()==2); | |||
TEST(*manager::instance().entries("foo").begin()=="bar::create"); | |||
TEST(*manager::instance().entries("foo").rbegin()=="lower"); | |||
} | |||
obj.close(); | |||
std::cout << "- Unload" << std::endl; | |||
try { | |||
cppcms::plugin::manager::entry<std::string(std::string const &)>("foo::lower"); | |||
manager::instance().entry<std::string(std::string const &)>("foo","lower"); | |||
std::cerr << "Must Not get there:" << __LINE__<<std::endl; | |||
return 1; | |||
} | |||
catch(cppcms::cppcms_error const &) {} | |||
catch(...) { throw std::runtime_error("Something else thrown"); } | |||
TEST(cppcms::plugin::manager::instance().has_plugin("foo")==false); | |||
} | |||
catch(std::exception const &e) { | |||
std::cerr << "Error:" << e.what() << std::endl; | |||
@@ -1,14 +1,28 @@ | |||
#include <cppcms/plugin.h> | |||
#include <ctype.h> | |||
#include "plugin_base.h" | |||
struct foo { | |||
static std::string lower(std::string f) | |||
namespace foo { | |||
std::string lower(std::string f) | |||
{ | |||
for(size_t i=0;i<f.size();i++) { | |||
f[i]=tolower(f[i]); | |||
} | |||
return f; | |||
} | |||
class bar : public bar_base { | |||
public: | |||
bar(std::string const &m) : msg_(m) {} | |||
virtual char const *msg() { return msg_.c_str(); }; | |||
static bar *create(std::string const &m) | |||
{ | |||
return new bar(m); | |||
} | |||
private: | |||
std::string msg_; | |||
}; | |||
}; | |||
CPPCMS_PLUGIN_ENTRY(std::string(std::string const &),foo::lower) | |||
CPPCMS_PLUGIN_ENTRY(foo,lower,std::string(std::string const &)) | |||
CPPCMS_PLUGIN_ENTRY(foo,bar::create,bar_base *(std::string const &)) |