Browse Source

Updates in plugin API

master
Artyom Beilis 8 years ago
parent
commit
91802d929b
4 changed files with 176 additions and 36 deletions
  1. +66
    -16
      cppcms/plugin.h
  2. +73
    -13
      src/plugin.cpp
  3. +20
    -4
      tests/plugin_test.cpp
  4. +17
    -3
      tests/test_plugin_so.cpp

+ 66
- 16
cppcms/plugin.h View File

@@ -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


+ 73
- 13
src/plugin.cpp View File

@@ -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

+ 20
- 4
tests/plugin_test.cpp View File

@@ -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;


+ 17
- 3
tests/test_plugin_so.cpp View File

@@ -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 &))

Loading…
Cancel
Save