From fe3feee67aeb73a777a5217ab99e37310f2fdd97 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 19 Nov 2017 01:43:19 +0100 Subject: [PATCH] Library: metafunction to detect support for a specific extension point such a detector function can be used to enable some template specialisation based on the fact that a target type exposes the desired extension point --- research/try.cpp | 10 ++ src/lib/meta/duck-detector.hpp | 29 ++++ tests/12metaprogramming.tests | 5 + .../meta/duck-detector-extension-test.cpp | 129 ++++++++++++++++++ tests/library/meta/duck-detector-test.cpp | 9 +- 5 files changed, 176 insertions(+), 6 deletions(-) create mode 100644 tests/library/meta/duck-detector-extension-test.cpp diff --git a/research/try.cpp b/research/try.cpp index bf14e0ec9..21c2c0fd5 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -38,7 +38,17 @@ /** @file try.cpp ** Metaprogramming: how to detect that a type in question exposes a free function extension point. + ** Since such an extension point is typically injected alongside with the type exposing the extension + ** point and intended to be picked up by ADL, all we have to check is if it is valid to invoke the + ** extension point function with an instance of the target type. ** + ** There are two difficulties to overcome, though + ** - a function might return void. And while we may indeed pick up `void` from `decltype(expr)`, + ** there is not much we can do with a void type. The remedy is just to use this type as template + ** parameter on another template instantiation, which fails if this type can not legally be formed. + ** - we do not know how to get a value of the type to probe, in order to feed it into the extension + ** point function. Fortunately, the `std::declval()` function was included into the C++ language + ** for this very purpose. */ typedef unsigned int uint; diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 6e71de863..436545c47 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -168,6 +168,35 @@ +/** Detector for support of a free-function extension point. + * Defines a metafunction (template), allowing to probe if the type + * in question supports a specific extension point function. Typically + * such functions are injected by some type in a way to be picked up by ADL. + * The detection test works by forming an expression to invoke the extension point, + * passing the type given as template parameter as function argument. If this expression + * type checks, the extension point is assumed to be supported. + * @warning beware of implicit type conversions + */ +#define META_DETECT_EXTENSION_POINT(_FUN_) \ + template \ + class HasExtensionPoint_##_FUN_ \ + { \ + template()))>\ + struct Probe \ + { }; \ + \ + template \ + static Yes_t check(Probe * ); \ + template \ + static No_t check(...); \ + \ + public: \ + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); \ + }; + + + /** Detector for a dereferentiation operator. Works like member detection */ #define META_DETECT_OPERATOR_DEREF() \ template \ diff --git a/tests/12metaprogramming.tests b/tests/12metaprogramming.tests index 85b50470a..ead44e8a4 100644 --- a/tests/12metaprogramming.tests +++ b/tests/12metaprogramming.tests @@ -116,6 +116,11 @@ return: 0 END +TEST "Duck detect extension points" DuckDetectorExtension_test <-<2>-<3>- out-lit: List2 :-<5>-<6>-<7>- diff --git a/tests/library/meta/duck-detector-extension-test.cpp b/tests/library/meta/duck-detector-extension-test.cpp new file mode 100644 index 000000000..558e80c3f --- /dev/null +++ b/tests/library/meta/duck-detector-extension-test.cpp @@ -0,0 +1,129 @@ +/* + DuckDetectorExtension(Test) - detecting support for extension points at compile time + + Copyright (C) Lumiera.org + 2017, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file duck-detector-extension-test.cpp + ** unit test \ref DuckDetectorExtension_test + */ + + +#include "lib/test/run.hpp" +#include "lib/meta/duck-detector.hpp" +#include "lib/util.hpp" + +#include + + +namespace lib { +namespace meta{ +namespace test{ + + using std::string; + + + namespace { // test types and definitions to explore.... + + void + fun (long) + { } + + int + fun (string, long) + { + return 12; + } + + void + fun () + { } + + + class Cheesy + { }; + + class Fishy + { + /** @note type Fishy exposes an extension point `fun` */ + friend void fun (Fishy&); + }; + + + }//(End) test definitions + + + + + /***********************************************************************************//** + * @test detect the presence of a free function extension point at compile time. + * It is a common idiom in C++ to expose an extension point through a free function, + * which is expected to be picked up by ADL. To mention a prominent example, any type + * can offer the ability to be _iterated_ by injecting free functions `begin(TY)` and + * `end(TY)`, to yield a STL compatible iterator. + * + * Since such an extension point is used just by invoking the _unqualified_ function + * with the target type, we can build a meta predicate based on the fact if such an + * function invocation expression can be formed for the type in question. + * + * @note the test really hinges on the ability to form the extension point call. + * For this reason, some implicit conversions might be involved, and some + * other conversions won't work (like passing a value to an extension point + * taking a reference). + */ + class DuckDetectorExtension_test : public Test + { + + META_DETECT_EXTENSION_POINT (funZ); + META_DETECT_EXTENSION_POINT (fun); + + + void + run (Arg) + { + fun (); + fun (23); + fun ("FUN", 45); + + CHECK ( not HasExtensionPoint_funZ::value ); + + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( not HasExtensionPoint_fun ::value ); + CHECK ( not HasExtensionPoint_fun ::value ); + + CHECK ( not HasExtensionPoint_fun ::value ); + CHECK ( not HasExtensionPoint_fun ::value ); + CHECK ( HasExtensionPoint_fun ::value ); + CHECK ( not HasExtensionPoint_fun ::value ); + CHECK ( not HasExtensionPoint_fun::value ); + } + }; + + + /** Register this test class... */ + LAUNCHER (DuckDetectorExtension_test, "unit meta"); + + + +}}} // namespace lib::meta::test diff --git a/tests/library/meta/duck-detector-test.cpp b/tests/library/meta/duck-detector-test.cpp index fe14f1be0..524393cf7 100644 --- a/tests/library/meta/duck-detector-test.cpp +++ b/tests/library/meta/duck-detector-test.cpp @@ -21,24 +21,21 @@ * *****************************************************/ /** @file duck-detector-test.cpp - ** unit test \ref DuckDetector_test + ** unit test \ref DuckDetector_test: + ** duck typing through metaprogramming */ #include "lib/test/run.hpp" #include "lib/meta/duck-detector.hpp" +#include "lib/format-cout.hpp" #include "lib/util.hpp" -#include - namespace lib { namespace meta{ namespace test{ - using std::cout; - using std::endl; - namespace { // some test ducks....