From b5c0158ec2d6bedcbb426cdd544f6cadcca0c932 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Mon, 3 Aug 2020 19:07:47 +0200 Subject: [PATCH] [libcalamares] Some if-method-exists trickery This builds some machinery so that we can create a detector for member-functions (methods) named . Use the macro to build the machinery: DECLARE_HAS_METHOD(myFunction) then after that, has_myFunction is either std::true_type or std::false_type depending on whether T has a method myFunction. --- src/libcalamares/utils/Tests.cpp | 53 ++++++++++++++++++++++ src/libcalamares/utils/Tests.h | 3 ++ src/libcalamares/utils/Traits.h | 77 ++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 src/libcalamares/utils/Traits.h diff --git a/src/libcalamares/utils/Tests.cpp b/src/libcalamares/utils/Tests.cpp index 120a9238b..300b9b531 100644 --- a/src/libcalamares/utils/Tests.cpp +++ b/src/libcalamares/utils/Tests.cpp @@ -27,6 +27,7 @@ #include "Entropy.h" #include "Logger.h" #include "RAII.h" +#include "Traits.h" #include "UMask.h" #include "Yaml.h" @@ -313,3 +314,55 @@ LibCalamaresTests::testBoolSetter() } QVERIFY( b ); } + +/* Demonstration of Traits support for has-a-method or not. + * + * We have two classes, c1 and c2; one has a method do_the_thing() and the + * other does not. A third class, Thinginator, has a method thingify(), + * which should call do_the_thing() of its argument if it exists. + */ + +struct c1 { int do_the_thing() { return 2; } }; +struct c2 { }; + +DECLARE_HAS_METHOD(do_the_thing) + +struct Thinginator +{ +public: + /// When class T has function do_the_thing() + template< class T > int thingify( T& t, const std::true_type& ) + { + return t.do_the_thing(); + } + + template< class T > int thingify( T&, const std::false_type& ) + { + return -1; + } + + template< class T > int thingify( T& t ) + { + return thingify(t, has_do_the_thing{}); + } +} ; + + +void +LibCalamaresTests::testTraits() +{ + has_do_the_thing x{}; + has_do_the_thing y{}; + + QVERIFY(x); + QVERIFY(!y); + + c1 c1{}; + c2 c2{}; + + QCOMPARE(c1.do_the_thing(), 2); + + Thinginator t; + QCOMPARE(t.thingify(c1), 2); // Calls c1::do_the_thing() + QCOMPARE(t.thingify(c2), -1); +} diff --git a/src/libcalamares/utils/Tests.h b/src/libcalamares/utils/Tests.h index 5a1f3528a..2d630392a 100644 --- a/src/libcalamares/utils/Tests.h +++ b/src/libcalamares/utils/Tests.h @@ -53,6 +53,9 @@ private Q_SLOTS: /** @brief Tests the RAII bits. */ void testBoolSetter(); + /** @brief Tests the Traits bits. */ + void testTraits(); + private: void recursiveCompareMap( const QVariantMap& a, const QVariantMap& b, int depth ); }; diff --git a/src/libcalamares/utils/Traits.h b/src/libcalamares/utils/Traits.h new file mode 100644 index 000000000..5d7061b7b --- /dev/null +++ b/src/libcalamares/utils/Traits.h @@ -0,0 +1,77 @@ +/* === This file is part of Calamares - === + * + * SPDX-FileCopyrightText: 2020 Adriaan de Groot + * + * Calamares 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 3 of the License, or + * (at your option) any later version. + * + * Calamares 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 Calamares. If not, see . + * + * SPDX-License-Identifier: GPL-3.0-or-later + * License-Filename: LICENSE + * + */ + +#ifndef UTILS_TRAITS_H +#define UTILS_TRAITS_H + +#include + + +namespace CalamaresUtils +{ + +/** @brief Traits machinery lives in this namespace + * + * The primary purpose of this namespace is to hold machinery that + * is created by the DECLARE_HAS_METHOD macro. + * + * The DECLARE_HAS_METHOD macro builds machinery to check whether + * a class has a particular named method. This can be used to + * specialize templates elsewhere for use with classes with, or without, + * the named method. + * + * To use the machinery (which is not that sophisticated): + * + * - Put `DECLARE_HAS_METHOD(myFunction)` somewhere in file scope. + * This puts together the machinery for detecting if `myFunction` + * is a method of some class. + * - At global scope, `has_myFunction` is now either std::true_type, + * if the type `T` has a method `T::myFunction`, or std::false_type, + * if it does not. + * + * To specialize template methods based on the presence of the named + * method, write **three** overloads: + * + * - `template myMethod(args ..., const std::true_type& )` + * This is the implementation where class T has `myFunction`. + * - `template myMethod(args ..., const std::false_type& )` + * This is the implementation without. + * - `template myMethod(args ...)` is the general implementation, + * which can call the specialized implementations with + * `return myMethod(args ..., has_myFunction{})` + */ +namespace Traits +{ +template< class > struct sfinae_true : std::true_type{}; +} +} + +#define DECLARE_HAS_METHOD(m) \ + namespace CalamaresUtils { namespace Traits { \ + struct has_ ## m { \ + template< class T > static auto f(int) -> sfinae_true; \ + template< class T > static auto f(long) -> std::false_type; \ + template< class T > using t = decltype( f (0) ); \ + }; } } \ + template< class T > using has_ ## m = CalamaresUtils::Traits:: has_ ## m ::t; + +#endif