ChipMaster's trial hacks on C++CMS starting with v1.2.1. Not sure I'll follow on with the v2 since it looks to be breaking and mostly frivolous.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

348 lines
11 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (C) 2008-2016 Artyom Beilis (Tonkikh) <artyomtnk@yahoo.com>
  4. //
  5. // See accompanying file COPYING.TXT file for licensing details.
  6. //
  7. ///////////////////////////////////////////////////////////////////////////////
  8. #ifndef CPPCMS_PLUGIN_H
  9. #define CPPCMS_PLUGIN_H
  10. #include <cppcms/defs.h>
  11. #include <cppcms/cppcms_error.h>
  12. #include <booster/callback.h>
  13. #include <booster/hold_ptr.h>
  14. #include <string>
  15. #include <set>
  16. namespace booster { class shared_object; }
  17. namespace cppcms {
  18. namespace json { class value; }
  19. ///
  20. /// \brief Plugin related API
  21. ///
  22. /// \ver{v1_2}
  23. namespace plugin {
  24. ///
  25. /// An exception that is thrown in case of actual function signature is not matching the requested one
  26. ///
  27. /// \ver{v1_2}
  28. class CPPCMS_API signature_error : public booster::bad_cast {
  29. public:
  30. signature_error(std::string const &msg);
  31. ~signature_error() throw();
  32. virtual char const *what() const throw();
  33. private:
  34. std::string msg_;
  35. };
  36. ///
  37. /// Class that esures that plugin is loaded and unloads it in destructor if needed
  38. ///
  39. /// Note: it tracks the loaded plugins by its name globally such that if another scope had loaded the plugin
  40. /// it wouldn't be loaded again.
  41. ///
  42. /// It is useable when plugin should be used outside of life scope of cppcms::service
  43. ///
  44. /// CppCMS configuration:
  45. ///
  46. /// - The search paths defined as array of strings in `plugin.paths` (optional)
  47. /// - List of modules defined as array of strings in `plugin.modules` (optional, if you want to call load later)
  48. /// - Shared object pattern defined as string in `plugin.shared_object_pattern` (optional)
  49. ///
  50. /// \ver{v1_2}
  51. class CPPCMS_API scope {
  52. scope(scope const &);
  53. void operator=(scope const &);
  54. public:
  55. ///
  56. /// Create an empty scope
  57. ///
  58. scope();
  59. ///
  60. /// Unloads all loaded plugins
  61. ///
  62. ~scope();
  63. ///
  64. /// Loads the plugins provided in main cppcms configuration file - argc,argv are same parameters as for cppcms::service constructor
  65. ///
  66. scope(int argc,char **argv);
  67. ///
  68. /// Loads the plugins provided in main cppcms configuration json file - same parameters as for cppcms::service constructor
  69. ///
  70. scope(json::value const &value);
  71. ///
  72. /// Set search path for plugins if undefined search according to the OS rules, if one of the paths in the vector is empty the search is performed by
  73. //// OS search rules
  74. ///
  75. void paths(std::vector<std::string> const &paths);
  76. ///
  77. /// Specify shared object/DLL naming convension. For example `lib{1}.dll` or `lib{1}.so` for converting the module name to shared object/dll name.
  78. ///
  79. /// Thus in the shared object \a pattern is `lib{1}.dll` that when module "foo" is loaded it tries to load `libfoo.dll` If not speficied default
  80. /// nameing is used, see booster::shared_object::name
  81. ///
  82. void shared_object_pattern(std::string const &pattern);
  83. ///
  84. /// Load specific module according to the paths and shared_object_pattern provided. Also note paths and pattern can be defined in cppcms configuration
  85. /// in the constructor
  86. ///
  87. /// \note module name isn't nessary same as plugin name. Module refers to name of shared object or dll while plugin is application defined. Same dll/so can
  88. /// contain multiple plugins or none.
  89. ///
  90. void load(std::string const &module);
  91. ///
  92. /// Check if the module was loaded withing any of the scopes - note it is static member function
  93. ///
  94. static bool is_loaded(std::string const &module);
  95. ///
  96. /// Get shared object loading withing \a this scope. If it wasn't loaded withing this scope throws cppcms_error
  97. ///
  98. booster::shared_object const &get(std::string const &module) const;
  99. ///
  100. /// Check if module is loaded withing this scope, unlike is_loaded that checks for the module globally, it refers to this scope only
  101. ///
  102. bool is_loaded_by_this_scope(std::string const &module) const;
  103. private:
  104. void init(json::value const &config);
  105. struct _class_data;
  106. static _class_data &class_data();
  107. struct _data;
  108. booster::hold_ptr<_data> d;
  109. };
  110. ///
  111. /// Central class that manages registration of plugins.
  112. ///
  113. /// It is used as singleton and accessed via manager::instance().
  114. ///
  115. /// Each plugin registers itself in the constructor and destructor implemented in shared library.
  116. ///
  117. ///
  118. /// \ver{v1_2}
  119. ///
  120. class CPPCMS_API manager {
  121. public:
  122. ///
  123. /// Get the instance of the manager
  124. ///
  125. static manager &instance();
  126. typedef booster::intrusive_ptr<booster::refcounted> refcounted_ptr;
  127. ///
  128. /// Functions registered as plugin entry points
  129. ///
  130. typedef refcounted_ptr (*entry_point_type)();
  131. ///
  132. /// Get plugin entry by \a plugin_name, \a entry_name and \a Signature
  133. ///
  134. /// If entry is not found or no entry point is created throws cppcms_error,if Signature mismatches the callback type
  135. /// throws booster::bad_cast
  136. ///
  137. /// For example
  138. /// \code
  139. /// booster::callback<cppcms::application *(cppcms::service &)> cb = :manager::instance().entry<cppcms::application *(cppcms::service &)>("foo","application");
  140. /// cppcms::application *app =cb(service());
  141. /// attach(app,"/plugins/foo(/.*)",1); // attach new application
  142. /// \endcode
  143. ///
  144. /// Or
  145. ///
  146. /// \code
  147. /// cppcms::application *app = manager::instance().entry<cppcms::application *(cppcms::service &)>("myapi","app::generator")(service());
  148. /// attach(app,"/plugins/foo(/.*)",1);
  149. /// \endcode
  150. ///
  151. template<typename Signature>
  152. booster::callback<Signature>
  153. entry(std::string const &plugin_name,std::string const &entry_name)
  154. {
  155. typedef booster::callback<Signature> callback_type;
  156. typedef typename callback_type::callable_type callable_type;
  157. typedef typename callback_type::pointer_type pointer_type;
  158. entry_point_type plugin_call = instance().get_entry(plugin_name,entry_name);
  159. if(!plugin_call)
  160. throw cppcms_error("Could not find entry `" + entry_name + "' in plugin `" + plugin_name + "'");
  161. refcounted_ptr call = plugin_call();
  162. if(!call)
  163. throw cppcms_error("Failed to create callback from plugin `"+plugin_name+"':entry `" + entry_name + "'");
  164. callable_type *real_call = dynamic_cast<callable_type *>(call.get());
  165. if(!real_call) {
  166. throw signature_error("Invalid signature request in plugin `"+ plugin_name +"':entry `"+entry_name+"', expected following signaure `" + instance().signature(plugin_name,entry_name) + "'");
  167. }
  168. pointer_type ptr(real_call);
  169. callback_type result(ptr);
  170. return result;
  171. }
  172. ///
  173. /// Check if plugin entry of type Signature exists in a plugin \a plugin named \a name
  174. ///
  175. template<typename Signature>
  176. bool has_entry(std::string const &plugin,std::string const &name)
  177. {
  178. typedef booster::callback<Signature> callback_type;
  179. entry_point_type plugin_call = get_entry(plugin,name);
  180. if(!plugin_call)
  181. return false;
  182. return dynamic_cast<callback_type *>(plugin_call().get())!=0;
  183. }
  184. ///
  185. /// Get entry point that creates a base of booster::callback::callable_type
  186. ///
  187. entry_point_type get_entry(std::string const &plugin,std::string const &name);
  188. ///
  189. /// Get textual representation of entry point signature - for logging purposes, if not found returns empty string
  190. ///
  191. std::string signature(std::string const &plugin,std::string const &name);
  192. ///
  193. /// Addes entry to the plugin manager - thread safe function
  194. ///
  195. void add_entry(char const *plugin_name,char const *entry_name,entry_point_type entry,char const *signature);
  196. ///
  197. /// Removes entry from the plugin manager - thread safe function
  198. ///
  199. void remove_entry(entry_point_type entry);
  200. ///
  201. /// Get list of all plugin names
  202. ///
  203. std::set<std::string> plugins();
  204. ///
  205. /// Get list of all entry names for \a plugin
  206. ///
  207. std::set<std::string> entries(std::string const &plugin);
  208. ///
  209. /// Returns true if plugin \a name is loaded
  210. ///
  211. bool has_plugin(std::string const &name);
  212. private:
  213. manager();
  214. ~manager();
  215. manager(manager const &);
  216. void operator=(manager const &);
  217. struct _data;
  218. struct entry_type;
  219. booster::hold_ptr<_data> d;
  220. };
  221. #define CPPCMS_PLUGIN_CONCAT(x,y) x ## y
  222. #define CPPCMS_PLUGIN_CONCAT2(x,y) CPPCMS_PLUGIN_CONCAT(x,y)
  223. ///
  224. /// Install generic plugin entry in plugin named \a plugin_name - string, the entry name \a entry_name - string
  225. /// and such that following expression is valid callback iniitalization:
  226. /// `booster::callback<type> cb = call;`
  227. ///
  228. /// For example
  229. /// \code
  230. /// class my_class : public plugin_api {
  231. /// public:
  232. /// statuc my_class *create(std::string const &parameter) { return new my_class(parameter); }
  233. /// ...
  234. /// };
  235. /// CPPCMS_PLUGIN_ENTRY("myplugin","api",&my_class::create,plugin_api *(std::string const &))
  236. /// }
  237. /// \endcode
  238. ///
  239. /// it is accessed as `manager::instance().entry<plugin_api *(std::string const &)>("myplugin","my_class::create")`
  240. ///
  241. /// \relates cppcms::plugin::manager
  242. ///
  243. #define CPPCMS_FULL_PLUGIN_ENTRY(plugin_name,entry_name,call,type) \
  244. namespace { \
  245. struct CPPCMS_PLUGIN_CONCAT2(stpg_ , __LINE__) { \
  246. static booster::intrusive_ptr<booster::refcounted> entry() \
  247. { \
  248. typedef booster::callback<type> ct; \
  249. ct cb = call; \
  250. booster::refcounted *tmp = cb.get_pointer().get(); \
  251. booster::intrusive_ptr<booster::refcounted> ptr(tmp); \
  252. return ptr; \
  253. } \
  254. CPPCMS_PLUGIN_CONCAT2(stpg_,__LINE__) () { \
  255. cppcms::plugin::manager::instance().add_entry( \
  256. plugin_name,entry_name,&entry,#type \
  257. ); \
  258. \
  259. } \
  260. ~CPPCMS_PLUGIN_CONCAT2(stpg_,__LINE__)() { \
  261. cppcms::plugin::manager::instance().remove_entry(&entry); \
  262. } \
  263. } CPPCMS_PLUGIN_CONCAT2(instance_of_stpg_,__LINE__); \
  264. }
  265. ///
  266. /// Install common function entry such that \a name is plugin name, \a call is entry name and `&name::call` is valid assignment
  267. /// for booster::callback<type>
  268. ///
  269. /// Usually name should be namespace or class name, call is function or static member functions
  270. ///
  271. /// For example
  272. /// \code
  273. /// namespace myplugin {
  274. /// class my_class : public plugin_api {
  275. /// public:
  276. /// statuc my_class *create(std::string const &parameter) { return new my_class(parameter); }
  277. /// ...
  278. /// };
  279. /// CPPCMS_PLUGIN_ENTRY(myplugin,my_class::create,plugin_api *(std::string const &))
  280. /// }
  281. /// \endcode
  282. ///
  283. /// it is accessed as `manager::instance().entry<plugin_api *(std::string const &)>("myplugin","my_class::create")`
  284. ///
  285. /// \relates cppcms::plugin::manager
  286. ///
  287. #define CPPCMS_PLUGIN_ENTRY(name,call,type) CPPCMS_FULL_PLUGIN_ENTRY(#name,#call,& name :: call,type)
  288. ///
  289. /// Install common function entry such that \a name is plugin name, \a entry is entry name and `&name::call` is valid assignment
  290. /// for booster::callback<type>
  291. ///
  292. /// Usually name should be namespace or class name, call is function or static member functions
  293. ///
  294. /// For example
  295. /// \code
  296. /// namespace myplugin {
  297. /// class my_class : public plugin_api {
  298. /// public:
  299. /// statuc my_class *create(std::string const &parameter) { return new my_class(parameter); }
  300. /// ...
  301. /// };
  302. /// CPPCMS_NAMED_PLUGIN_ENTRY(myplugin,api,my_class::create,plugin_api *(std::string const &))
  303. /// }
  304. /// \endcode
  305. ///
  306. /// it is accessed as `manager::instance().entry<plugin_api *(std::string const &)>("myplugin","api")`
  307. ///
  308. /// \relates cppcms::plugin::manager
  309. ///
  310. #define CPPCMS_NAMED_PLUGIN_ENTRY(name,entry,call,type) CPPCMS_FULL_PLUGIN_ENTRY(#name,#entry,& name :: call,type)
  311. } // plugin
  312. } // cppcms
  313. #endif