4

Is there a way to implement universal and existential quantification using C++ template magic (maybe using SFINAE etc.)? Something like this:

template
    <
        template <typename Argument> class Predicate
    >
struct UniversalQuantification
{
    static const bool value =
        /*for any Argument Predicate<Argument>::value == true ? true : false*/;
};

template
    <
        template <typename Argument> class Predicate
    >
struct ExistentialQuantification
{
    static const bool value =
        /*for some Argument Predicate<Argument>::value == true ? true : false*/;
};
Sebastian Hoffmann
  • 11,127
  • 7
  • 49
  • 77
Constructor
  • 7,273
  • 2
  • 24
  • 66
  • I believe this is simply not possible. What are you *really* trying to do? – iavr Mar 25 '14 at 18:37
  • @iavr This time it is a *purely* theoretical question. But I'll be happy to see all possible solutions, even non-ideal and approximate. I hope that there is at least some decision. – Constructor Mar 25 '14 at 18:45
  • How should these quantifications be used? – nosid Mar 25 '14 at 18:45
  • @nosid Suppose you have a class template with one type parameter: `template class Predicate;` which has a static field `static const bool value;` (possibly its values are different for different template arguments). You want to check at a compile time that `value` is true for all possible `Predicate` template arguments (universal quantification). Or there exists at least one argument for which `value` is true (existential quantification). – Constructor Mar 25 '14 at 18:53
  • 1
    At runtime possible, at compile-time impossible IMO – Sebastian Hoffmann Mar 25 '14 at 18:57
  • So the question is, if the predicate is _true_ for **all** types? The number of types is infinite, and not all types are known at compile-time. So, that's impossible. – nosid Mar 25 '14 at 19:02
  • @Paranaix It is not interesting at run-time. – Constructor Mar 25 '14 at 19:04
  • @nosid The number of types is **not** infinite, but it's large enough :-) – iavr Mar 25 '14 at 19:04
  • @nosid If you say about universal quantification then yes, you are right. Predicate should be true for all types. All types in C++ are known at compile-time and their quantity is not infinite, of course. :-) – Constructor Mar 25 '14 at 19:07
  • @Constructor So do you suggest we begin instantiating with a number of types to see what happens? Would we then need to enumerate all types present in the current compilation unit? Would you allow any constraints that may limit the number? – iavr Mar 25 '14 at 19:12
  • @iavr Solutions with all possible reasonable constraints are welcome, too. – Constructor Mar 25 '14 at 19:15
  • @Constructor: At compile-time, you only see the types of the same compilation unit, but there can be further types in different compilation units. And regarding the number of types: With `template struct Ints { };`, you already have a centillion different types. – nosid Mar 25 '14 at 19:17
  • @nosid Yes, I think it is sufficient to restrict the problem to the types from the same compilation unit. And centillion is not infinity. – Constructor Mar 25 '14 at 19:25
  • 2
    ExistentialQuantification is very tied to the halting problem... – Mooing Duck Mar 25 '14 at 19:26
  • @MooingDuck Well, as a constraint we could exclude testing "smart" functions like `UniversalQuantification` and `ExistentialQuantification` themselves :-) Not that it's much easier now! – iavr Mar 25 '14 at 19:28
  • @Constructor: In many cases, the number of types is theoretically infinite (it's not at all hard to define `succ` in TMP), and only bounded by the template recursion depth. But if you have types right up to the limit of template recursion, you can't pass all those types to another template for consideration... – Ben Voigt Mar 25 '14 at 19:37
  • @MooingDuck Template parameter of `ExistentialQuantification` class template is a class template with a *type* parameter. So I don't understand why the halting problem is tied to it. – Constructor Mar 25 '14 at 19:45
  • @BenVoigt We all use computers with finite memory so all theoretical infinities become finite at practice. I think we can assume that the quantity of types is not big enough to allow the occurrence of the problems with the limit of template recursion. – Constructor Mar 25 '14 at 19:53
  • @Constructor You can wrap a `template – iavr Mar 25 '14 at 21:28
  • @iavr Do you mean something like `template struct Wrapper { static const bool value = ExistentialQuantification::value; };`? – Constructor Mar 25 '14 at 21:35
  • 1
    @Constructor It's large, but here it is: `template – iavr Mar 25 '14 at 21:58

4 Answers4

3

If you pass a finite set of possible template arguments to the template, it is indeed possible to evaluate this at compile-time. However, it is impossible to evaluate this for every argument the passed template has been used with, as these types/args are unknown.

This is the solution for a finite set of arguments for the ExistentialQuantification class. In order to achieve the behaviour of the UniversalQuantification class, you simple have to change the || to a &&:

template<typename Arg>
struct Pred1;

template<>
struct Pred1<float> { static const bool value = true; };

template<>
struct Pred1<double> { static const bool value = false; };

template<>
struct Pred1<long> { static const bool value = true; };


template<template <typename Argument> class Predicate, typename... Types>
struct ExistentialQuantification;

template<template <typename Argument> class Predicate, typename Arg>
struct ExistentialQuantification<Predicate, Arg>
{
    static const bool value = Predicate<Arg>::value;
};

template<template <typename Argument> class Predicate, typename Arg, typename... Types>
struct ExistentialQuantification<Predicate, Arg, Types...>
{
    static const bool value = Predicate<Arg>::value || ExistentialQuantification<Predicate, Types...>::value;
};

int main()
{
    std::cout << ExistentialQuantification<Pred1, long, double, float>::value << std::endl;
}

In this example, value will evaluate to true || false || true and is thus true of course.

Sebastian Hoffmann
  • 11,127
  • 7
  • 49
  • 77
  • Thank you. It is an obvious solution for a finite set of types. And you forgot a semicolon after `template<> struct Pred1` specialization. – Constructor Mar 25 '14 at 19:32
3

Ok, if we're just being smart here, and if we're allowed a couple of constraints, here are two constraints on the programmer, that really solve the problem, smoothly.

  1. if a predicate is needed in the code that is always true, only use the following:

    template<typename> struct always : std::true_type { };

  2. if a predicate is needed in the code that is never true, only use the following:

    template<typename> struct never : std::false_type { };

Now, the solution is simple:

template<template<typename> class>
struct UniversalQuantification : std::false_type { };

template<>
struct UniversalQuantification<always> : std::true_type { };

template<template<typename> class>
struct ExistentialQuantification : std::true_type { };

template<>
struct ExistentialQuantification<never> : std::false_type { };
iavr
  • 7,547
  • 1
  • 18
  • 53
1

Using a library like Boost.MPL, if you can put your universe of allowed types into a type list, e.g. in a boost::mpl::vector, then your quantifiers are just the compile-time version of std::all_of and std::any_of. You can define them as:

#include <ios>
#include <iostream>
#include <type_traits>                  // is_same, is_base_of
#include <boost/mpl/end.hpp>            // end
#include <boost/mpl/find_if.hpp>        // find_if
#include <boost/mpl/lambda.hpp>         // lambda
#include <boost/mpl/logical.hpp>        // not_
#include <boost/mpl/placeholders.hpp>   // _1
#include <boost/mpl/vector.hpp>         // vector

template<typename Sequence, typename Pred>
struct all_of
:
        std::is_same< typename
                boost::mpl::find_if<
                        Sequence,
                        boost::mpl::not_<Pred>
                >::type, typename
                boost::mpl::end<Sequence>::type
        >
{};

template<typename Sequence, typename Pred>
struct none_of
:
        all_of< Sequence, boost::mpl::not_< Pred > >
{};

template<typename Sequence, typename Pred>
struct any_of
:
        boost::mpl::not_< none_of< Sequence, Pred > >
{};

struct B {}; 
struct D : B {};
struct X {};

using Universe = boost::mpl::vector<B, D, X>;
using Predicate = boost::mpl::lambda<std::is_base_of<B, boost::mpl::_1>>;

int main()
{
    std::cout << std::boolalpha;
    std::cout << all_of<Universe, Predicate>{} << "\n";
    std::cout << any_of<Universe, Predicate>{} << "\n";
}

Live Example.

As you can see, because X does not have B as a base class, the universal quantification fails, but because D does have B as a base class, the existential quantification succeeds.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • Thank you. I think it will take time to realize this solution. Is this solution equivalent to the solution using C++11 variadic templates (possibly involving `std::tuple`)? – Constructor Mar 25 '14 at 19:23
  • @Constructor modified my example to work live on Coliru. I would not recommend rolling your own. Boost.MPL is a very nice library once you get the hang of it, and it works even with C++98. – TemplateRex Mar 25 '14 at 19:29
  • Thank you for the example. Boost is a very nice library, I agree with you. – Constructor Mar 25 '14 at 19:36
1

Well, if we're just talking about n-ary (variadic) functions and, or as in the answer by TemplateRex, here's my preferred way without Boost:

using _true  = std::integral_constant <bool, true>;
using _false = std::integral_constant <bool, false>;

template <bool C, typename T, typename E>
using _if = typename std::conditional <C, T, E>::type;

template <typename...> struct _and;
template <typename...> struct _or;

template <typename A, typename... B>
struct _and <A, B...> : _if <A{}, _and <B...>, _false> { };

template <typename A, typename... B>
struct _or <A, B...> : _if <A{}, _true, _or <B...> > { };

template <> struct _and <> : _true { };
template <> struct _or <> : _false { };
Community
  • 1
  • 1
iavr
  • 7,547
  • 1
  • 18
  • 53
  • Why do you not use `std::conditional` instead of your `_if_t` class template? And I don't see any predicate template in your solution. :-) – Constructor Mar 25 '14 at 20:05
  • Right, see edited code (it was just a matter of habit, doing everything from scratch!) You don't see a predicate template because it's only part of a solution :-) It doesn't matter, I'm preparing another solution now. – iavr Mar 25 '14 at 20:10
  • I'm looking forward to it. – Constructor Mar 25 '14 at 20:16