Line data Source code
1 : // Copyright (c) 2013-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/cppthread
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : #pragma once
20 :
21 : // self
22 : //
23 : #include "cppthread/mutex.h"
24 :
25 : #include "cppthread/exception.h"
26 : #include "cppthread/version.h" // used in CPPTHREAD_PLUGIN_START()
27 :
28 :
29 : // snapdev lib
30 : //
31 : #include <snapdev/not_used.h>
32 :
33 :
34 : // C++ set
35 : //
36 : #include <map>
37 : #include <memory>
38 : #include <set>
39 : #include <string>
40 : #include <vector>
41 :
42 :
43 : namespace cppthread
44 : {
45 :
46 :
47 : namespace detail
48 : {
49 : class plugin_repository;
50 : } // detail namespace
51 :
52 :
53 : typedef std::set<std::string> string_set_t;
54 :
55 :
56 : template<int N>
57 8 : constexpr char const * validate_name(char const (&str)[N])
58 : {
59 : static_assert(N - 1 > 0);
60 :
61 8 : if(str[0] != '_'
62 8 : && (str[0] < 'a' || str[0] > 'z')
63 0 : && (str[0] < 'A' || str[0] > 'Z'))
64 : {
65 : throw cppthread_logic_error(
66 : "first character of name \""
67 : + std::string(str)
68 0 : + "\" not valid.");
69 : }
70 :
71 62 : for(int i(1); i < N - 1; ++i)
72 : {
73 54 : if(str[i] != '_'
74 52 : && (str[i] < 'a' || str[i] > 'z')
75 0 : && (str[i] < 'A' || str[i] > 'Z')
76 0 : && (str[i] < '0' || str[i] > '9'))
77 : {
78 : throw cppthread_logic_error(
79 : "character #"
80 : + std::to_string(i)
81 : + " ("
82 0 : + str[i]
83 : + ") of name \""
84 : + str
85 0 : + "\" not valid.");
86 : }
87 : }
88 :
89 8 : return str;
90 : }
91 :
92 :
93 1 : constexpr time_t validate_date(time_t date)
94 : {
95 : // any new plugin must be created after this date (about 2021/06/22 10:30)
96 : //
97 1 : if(date < 1624382757LL)
98 : {
99 0 : throw cppthread_out_of_range("plugin dates are expected to be at least 2021/06/22 10:30");
100 : }
101 1 : return date;
102 : }
103 :
104 :
105 : template<typename T>
106 2 : constexpr void validate_version(T const major, T const minor)
107 : {
108 2 : if(major == 0 && minor == 0)
109 : {
110 0 : throw cppthread_logic_error("the plugin version cannot be 0.0.");
111 : }
112 2 : }
113 :
114 :
115 :
116 :
117 : struct version_t
118 : {
119 : constexpr version_t()
120 : : f_major(0)
121 : , f_minor(0)
122 : , f_patch(0)
123 : {
124 : }
125 :
126 2 : version_t(
127 : std::int32_t major
128 : , std::int32_t minor
129 : , std::int32_t patch = 0)
130 2 : : f_major(major)
131 : , f_minor(minor)
132 2 : , f_patch(patch)
133 : {
134 2 : validate_version(major, minor);
135 2 : }
136 :
137 : std::int32_t f_major = 0;
138 : std::int32_t f_minor = 0;
139 : std::int32_t f_patch = 0;
140 : };
141 :
142 :
143 1 : struct plugin_definition
144 : {
145 : version_t f_version = version_t();
146 : version_t f_library_version = version_t();
147 : time_t f_last_modification = 0; // uses the compilation date & time converted to a Unix date
148 : std::string f_name = std::string();
149 : std::string f_description = std::string();
150 : std::string f_help_uri = std::string();
151 : std::string f_icon = std::string();
152 : string_set_t f_categorization_tags = string_set_t();
153 : string_set_t f_dependencies = string_set_t();
154 : string_set_t f_conflicts = string_set_t();
155 : string_set_t f_suggestions = string_set_t();
156 : };
157 :
158 :
159 :
160 :
161 :
162 : template<typename T>
163 : class plugin_definition_value
164 : {
165 : public:
166 : typedef T value_t;
167 :
168 17 : constexpr plugin_definition_value<T>(T const v)
169 17 : : f_value(v)
170 : {
171 17 : }
172 :
173 14 : constexpr value_t get() const
174 : {
175 14 : return f_value;
176 : }
177 :
178 : private:
179 : value_t f_value = value_t();
180 : };
181 :
182 :
183 :
184 :
185 : class plugin_version
186 : : public plugin_definition_value<version_t>
187 : {
188 : public:
189 : constexpr plugin_version()
190 : : plugin_definition_value<version_t>(version_t())
191 : {
192 : }
193 :
194 1 : constexpr plugin_version(version_t version)
195 1 : : plugin_definition_value<version_t>(version)
196 : {
197 1 : }
198 : };
199 :
200 :
201 : class plugin_library_version
202 : : public plugin_definition_value<version_t>
203 : {
204 : public:
205 : constexpr plugin_library_version()
206 : : plugin_definition_value<version_t>(version_t())
207 : {
208 : }
209 :
210 1 : constexpr plugin_library_version(version_t version)
211 1 : : plugin_definition_value<version_t>(version)
212 : {
213 1 : }
214 : };
215 :
216 :
217 : class plugin_last_modification
218 : : public plugin_definition_value<time_t>
219 : {
220 : public:
221 : constexpr plugin_last_modification()
222 : : plugin_definition_value<time_t>(time_t())
223 : {
224 : }
225 :
226 1 : constexpr plugin_last_modification(time_t const last_modification)
227 1 : : plugin_definition_value<time_t>(validate_date(last_modification))
228 : {
229 1 : }
230 : };
231 :
232 :
233 : class plugin_name
234 : : public plugin_definition_value<char const *>
235 : {
236 : public:
237 : constexpr plugin_name()
238 : : plugin_definition_value<char const *>(nullptr)
239 : {
240 : }
241 :
242 : template<int N>
243 1 : constexpr plugin_name(char const (&name)[N])
244 1 : : plugin_definition_value<char const *>(validate_name(name))
245 : {
246 1 : }
247 : };
248 :
249 :
250 : class plugin_description
251 : : public plugin_definition_value<char const *>
252 : {
253 : public:
254 1 : constexpr plugin_description()
255 1 : : plugin_definition_value<char const *>(nullptr)
256 : {
257 1 : }
258 :
259 1 : constexpr plugin_description(char const * description)
260 1 : : plugin_definition_value<char const *>(description)
261 : {
262 1 : }
263 : };
264 :
265 :
266 : class plugin_help_uri
267 : : public plugin_definition_value<char const *>
268 : {
269 : public:
270 1 : constexpr plugin_help_uri()
271 1 : : plugin_definition_value<char const *>(nullptr)
272 : {
273 1 : }
274 :
275 1 : constexpr plugin_help_uri(char const * help_uri)
276 1 : : plugin_definition_value<char const *>(help_uri)
277 : {
278 1 : }
279 : };
280 :
281 :
282 : class plugin_icon
283 : : public plugin_definition_value<char const *>
284 : {
285 : public:
286 1 : constexpr plugin_icon()
287 1 : : plugin_definition_value<char const *>(nullptr)
288 : {
289 1 : }
290 :
291 1 : constexpr plugin_icon(char const * icon)
292 1 : : plugin_definition_value<char const *>(icon)
293 : {
294 1 : }
295 : };
296 :
297 :
298 : class plugin_categorization_tag
299 : : public plugin_definition_value<char const *>
300 : {
301 : public:
302 : //constexpr plugin_categorization_tag()
303 : // : plugin_definition_value<char const *>()
304 : //{
305 : //}
306 :
307 : template<int N>
308 3 : constexpr plugin_categorization_tag(char const (&tag)[N])
309 3 : : plugin_definition_value<char const *>(validate_name(tag))
310 : {
311 3 : }
312 : };
313 :
314 :
315 : class plugin_dependencies
316 : : public plugin_definition_value<char const *>
317 : {
318 : public:
319 : constexpr plugin_dependencies()
320 : : plugin_definition_value<char const *>(nullptr)
321 : {
322 : }
323 :
324 : template<int N>
325 : constexpr plugin_dependencies(char const (&dependencies)[N])
326 : : plugin_definition_value<char const *>(validate_name(dependencies))
327 : {
328 : }
329 : };
330 :
331 :
332 : class plugin_conflicts
333 : : public plugin_definition_value<char const *>
334 : {
335 : public:
336 : constexpr plugin_conflicts()
337 : : plugin_definition_value<char const *>(nullptr)
338 : {
339 : }
340 :
341 : template<int N>
342 3 : constexpr plugin_conflicts(char const (&conflicts)[N])
343 3 : : plugin_definition_value<char const *>(validate_name(conflicts))
344 : {
345 3 : }
346 : };
347 :
348 :
349 : class plugin_suggestions
350 : : public plugin_definition_value<char const *>
351 : {
352 : public:
353 : constexpr plugin_suggestions()
354 : : plugin_definition_value<char const *>(nullptr)
355 : {
356 : }
357 :
358 : template<int N>
359 1 : constexpr plugin_suggestions(char const (&suggestions)[N])
360 1 : : plugin_definition_value<char const *>(validate_name(suggestions))
361 : {
362 1 : }
363 : };
364 :
365 :
366 :
367 :
368 :
369 :
370 : template<typename T, typename F, class ...ARGS>
371 7 : constexpr typename std::enable_if<std::is_same<T, F>::value, typename T::value_t>::type find_plugin_information(F first, ARGS ...args)
372 : {
373 7 : snap::NOT_USED(args...);
374 7 : return first.get();
375 : }
376 :
377 :
378 : template<typename T, typename F, class ...ARGS>
379 21 : constexpr typename std::enable_if<!std::is_same<T, F>::value, typename T::value_t>::type find_plugin_information(F first, ARGS ...args)
380 : {
381 21 : snap::NOT_USED(first);
382 21 : return find_plugin_information<T>(args...);
383 : }
384 :
385 :
386 :
387 :
388 :
389 : template<typename T, typename F>
390 1 : typename std::enable_if<std::is_same<T, F>::value, string_set_t>::type find_plugin_set(F first)
391 : {
392 1 : string_set_t s;
393 1 : s.insert(first.get());
394 1 : return s;
395 : }
396 :
397 :
398 : template<typename T, typename F>
399 3 : typename std::enable_if<!std::is_same<T, F>::value, string_set_t>::type find_plugin_set(F first)
400 : {
401 3 : snap::NOT_USED(first);
402 3 : return string_set_t();
403 : }
404 :
405 :
406 : template<typename T, typename F, class ...ARGS>
407 6 : typename std::enable_if<std::is_same<T, F>::value && (sizeof...(ARGS) > 0), string_set_t>::type find_plugin_set(F first, ARGS ...args)
408 : {
409 6 : string_set_t s(find_plugin_set<T>(args...));
410 6 : s.insert(first.get());
411 6 : return s;
412 : }
413 :
414 :
415 : template<typename T, typename F, class ...ARGS>
416 46 : typename std::enable_if<!std::is_same<T, F>::value && (sizeof...(ARGS) > 0), string_set_t>::type find_plugin_set(F first, ARGS ...args)
417 : {
418 46 : snap::NOT_USED(first);
419 46 : return find_plugin_set<T>(args...);
420 : }
421 :
422 :
423 :
424 : template<class ...ARGS>
425 1 : constexpr plugin_definition define_plugin(ARGS ...args)
426 : {
427 : #pragma GCC diagnostic push
428 : #pragma GCC diagnostic ignored "-Wpedantic"
429 5 : plugin_definition def =
430 : {
431 : .f_version = find_plugin_information<plugin_version>(args...), // no default, must be defined
432 : .f_library_version = find_plugin_information<plugin_library_version>(args...), // no default, must be defined
433 1 : .f_last_modification = find_plugin_information<plugin_last_modification>(args...), // no default, must be defined
434 : .f_name = find_plugin_information<plugin_name>(args...), // no default, must be defined
435 1 : .f_description = find_plugin_information<plugin_description>(args..., plugin_description()),
436 1 : .f_help_uri = find_plugin_information<plugin_help_uri>(args..., plugin_help_uri()),
437 1 : .f_icon = find_plugin_information<plugin_icon>(args..., plugin_icon()),
438 : .f_categorization_tags = find_plugin_set<plugin_categorization_tag>(args...),
439 : .f_dependencies = find_plugin_set<plugin_dependencies>(args...),
440 : .f_conflicts = find_plugin_set<plugin_conflicts>(args...),
441 : .f_suggestions = find_plugin_set<plugin_suggestions>(args...),
442 : };
443 : #pragma GCC diagnostic pop
444 :
445 1 : return def;
446 : }
447 :
448 :
449 :
450 :
451 :
452 :
453 88 : class plugin_paths
454 : {
455 : public:
456 : typedef std::string path_t;
457 : typedef std::vector<std::string> paths_t;
458 :
459 : std::size_t size() const;
460 : std::string at(std::size_t idx) const;
461 : void set_allow_redirects(bool allow = true);
462 : bool get_allow_redirects() const;
463 : path_t canonicalize(path_t const & path);
464 : void push(path_t const & path);
465 : void add(std::string const & paths);
466 : void erase(std::string const & path);
467 :
468 : private:
469 : paths_t f_paths = paths_t();
470 : bool f_allow_redirects = false;
471 : };
472 :
473 :
474 12 : class plugin_names
475 : {
476 : public:
477 : typedef std::string name_t;
478 : typedef std::string filename_t;
479 : typedef std::map<name_t, filename_t> names_t;
480 :
481 : plugin_names(plugin_paths const & paths, bool script_names = false);
482 :
483 : bool validate(name_t const & name);
484 : bool is_emcascript_reserved(std::string const & word);
485 :
486 : filename_t to_filename(name_t const & name);
487 : void push(name_t const & name);
488 : void add(std::string const & set);
489 : names_t names() const;
490 :
491 : void find_plugins(name_t const & prefix = name_t(), name_t const & suffix = name_t());
492 :
493 : private:
494 : plugin_paths const f_paths;
495 : bool const f_prevent_script_names = false;
496 : names_t f_names = names_t();
497 : };
498 :
499 :
500 :
501 :
502 :
503 : class plugin;
504 :
505 :
506 : class plugin_factory
507 : {
508 : public:
509 : plugin_factory(plugin_definition const & definition, std::shared_ptr<plugin> instance);
510 : ~plugin_factory();
511 : plugin_factory(plugin_factory const &) = delete;
512 : plugin_factory & operator = (plugin_factory const &) = delete;
513 :
514 : plugin_definition const & definition() const;
515 : std::shared_ptr<plugin> instance() const;
516 : void register_plugin(char const * name, std::shared_ptr<plugin> p);
517 :
518 : private:
519 : plugin_definition const & f_definition;
520 : std::shared_ptr<plugin> f_plugin = std::shared_ptr<plugin>();
521 : };
522 :
523 :
524 :
525 :
526 : class plugin
527 : {
528 : public:
529 : typedef std::shared_ptr<plugin> pointer_t;
530 : typedef std::vector<pointer_t> vector_t; // sorted by dependencies & then by name
531 : typedef std::map<std::string, pointer_t>
532 : map_t; // sorted by name
533 :
534 : plugin(plugin_factory const & factory);
535 : plugin(plugin const &) = delete;
536 : virtual ~plugin();
537 : plugin & operator = (plugin const &) = delete;
538 :
539 : version_t version() const;
540 : time_t last_modification() const;
541 : plugin_names::name_t name() const;
542 : plugin_names::filename_t filename() const;
543 : std::string description() const;
544 : std::string help_uri() const;
545 : std::string icon() const;
546 : string_set_t categorization_tags() const;
547 : string_set_t dependencies() const;
548 : string_set_t conflicts() const;
549 : string_set_t suggestions() const;
550 :
551 : virtual void bootstrap(void * data);
552 :
553 : private:
554 : friend class detail::plugin_repository;
555 :
556 : plugin_factory const & f_factory;
557 : plugin_names::filename_t f_filename = std::string();
558 : };
559 :
560 :
561 : #define CPPTHREAD_PLUGIN_DEFAULTS(name) \
562 : public: \
563 : typedef std::shared_ptr<name> pointer_t; \
564 : name(); \
565 : virtual ~name(); \
566 : static pointer_t instance();
567 :
568 :
569 :
570 :
571 1 : class plugin_collection
572 : {
573 : public:
574 : typedef std::shared_ptr<plugin_collection> pointer_t;
575 :
576 : plugin_collection(plugin_names const & names);
577 : plugin_collection(plugin_collection const &) = delete;
578 : plugin_collection & operator = (plugin_collection const &) = delete;
579 :
580 : bool load_plugins();
581 : bool is_loaded(std::string const & name) const;
582 :
583 : /** \brief Retrieve a plugin from this collection.
584 : *
585 : * This function searches for a plugin by the given name in this collection.
586 : *
587 : * Note that different collections can share the same plugin (if the filenames
588 : * are the same) and one collection may know about a plugin and another
589 : * collection may not know about a plugin. At this time, the library does not
590 : * offer a direct access to the global list so you can't determine whether
591 : * a specific plugin is loaded through a plugin_collection.
592 : *
593 : * \param[in] name The name of the plugin to search.
594 : *
595 : * \return The pointer to the plugin if found, nullptr otherwise.
596 : */
597 : template<class T>
598 1 : typename T::pointer_t get_plugin_by_name(std::string const & name)
599 : {
600 1 : auto it(f_plugins_by_name.find(name));
601 1 : if(it != f_plugins_by_name.end())
602 : {
603 1 : return std::static_pointer_cast<T>(it->second);
604 : }
605 :
606 0 : return typename T::pointer_t();
607 : }
608 :
609 : void set_data(void * data);
610 :
611 : private:
612 : mutex f_mutex = mutex();
613 : plugin_names f_names;
614 : plugin::map_t f_plugins_by_name = plugin::map_t(); // plugins sorted by name only
615 : plugin::vector_t f_ordered_plugins = plugin::vector_t(); // sorted plugins
616 : void * f_data = nullptr;
617 : };
618 :
619 :
620 :
621 :
622 : #define CPPTHREAD_PLUGIN_START(name, major, minor) \
623 : ::cppthread::plugin_definition const g_plugin_##name##_definition = ::cppthread::define_plugin( \
624 : ::cppthread::plugin_version(::cppthread::version_t(major, minor, 0)) \
625 : , ::cppthread::plugin_library_version(::cppthread::version_t(CPPTHREAD_VERSION_MAJOR, CPPTHREAD_VERSION_MINOR, CPPTHREAD_VERSION_PATCH)) \
626 : , ::cppthread::plugin_last_modification(UTC_BUILD_TIME_STAMP) \
627 : , ::cppthread::plugin_name(#name)
628 :
629 :
630 : #define CPPTHREAD_PLUGIN_END(name) \
631 : ); \
632 : class plugin_##name##_factory : public ::cppthread::plugin_factory { \
633 : public: plugin_##name##_factory() \
634 : : plugin_factory(g_plugin_##name##_definition, std::make_shared<name>()) \
635 : { register_plugin(#name, instance()); } \
636 : plugin_##name##_factory(plugin_##name##_factory const &) = delete; \
637 : plugin_##name##_factory & operator = (plugin_##name##_factory const &) = delete; \
638 : } g_plugin_##name##_factory; \
639 : name::name() : plugin(g_plugin_##name##_factory) {} \
640 : name::~name() {} \
641 : name::pointer_t name::instance() { return std::static_pointer_cast<name>(g_plugin_##name##_factory.instance()); }
642 :
643 :
644 :
645 :
646 : } // namespace cppthread
647 : // vim: ts=4 sw=4 et
|