iffl  1.3.4
Implements Intrusive Flat Forward List container
Classes | Namespaces | Typedefs | Variables
iffl_mpl.h File Reference

This module contains few meta-programming facilities that are not part of the STL. More...

#include <iffl_config.h>
#include <type_traits>

Go to the source code of this file.

Classes

class  iffl::mpl::nonesuch
 A valid type that cannot be instantiate. Used by some detector meta-functions as a default type. More...
 
class  iffl::mpl::details::detector< Default, AlwaysVoid, Operation, Arguments >
 defines detector meta-function More...
 
struct  iffl::mpl::details::detector< Default, void_t< Operation< Arguments... > >, Operation, Arguments... >
 

Namespaces

 iffl::mpl
 intrusive flat forward list
 
 iffl
 intrusive flat forward list
 
 iffl::mpl::details
 intrusive flat forward list meta-programing helpers library internal types
 

Typedefs

template<typename... Whatever>
using iffl::mpl::void_t = std::void_t< Whatever ... >
 Type that can take any number of template parameters and returns void. It is used by detector meta-function SFINAE.
 
template<template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::is_detected = typename details::detector< nonesuch, void, Operation, Arguments... >::value_type
 is_detected will be either true_type or false_type, depending if Operation<Arguments...> meta-function is well formed More...
 
template<template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::is_detected_t = is_detected< Operation, Arguments... >
 alias for is_detected More...
 
template<template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::detected_t = typename details::detector< nonesuch, void, Operation, Arguments... >::type
 is_detected will be either true_type or false_type, depending if Operation<Arguments...> meta-function is well formed More...
 
template<typename Default , template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::detected_or = details::detector< Default, void, Operation, Arguments... >
 type of detector Operation<Arguments...> if it is well formed, and otherwise Default type More...
 
template<typename Default , template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::detected_or_t = typename detected_or< Default, Operation, Arguments... >::type
 Extracts and return detector<...>::type of detected_or. More...
 
template<typename ExpectedType , template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::is_detected_exact = std::is_same< ExpectedType, detected_t< Operation, Arguments... > >
 returns std::true_type when result of meta-function detected_t is the same as ExpectedType More...
 
template<typename ConvertibleToType , template< typename... > typename Operation, typename... Arguments>
using iffl::mpl::is_detected_convertible = std::is_convertible< detected_t< Operation, Arguments... >, ConvertibleToType >
 returns std::true_type when result of meta-function detected_t is the convertible to ConvertibleToType More...
 

Variables

template<template< typename... > typename Operation, typename... Arguments>
constexpr auto const iffl::mpl::is_detected_v { is_detected<Operation, Arguments...>{} }
 Instance of is_detected meta-function output. More...
 
template<template< typename... > typename Operation, typename... Arguments>
constexpr auto const iffl::mpl::detected_v { detected_t<Operation, Arguments...>{} }
 Instance of detected_t meta-function output. More...
 
template<typename Default , template< typename... > typename Operation, typename... Arguments>
constexpr auto const iffl::mpl::detected_or_v { typename detected_or<Default, Operation, Arguments...>::type{} }
 instance of detector<...>::type of detected_or More...
 
template<typename ExpectedType , template< typename... > typename Operation, typename... Arguments>
constexpr auto const iffl::mpl::is_detected_exact_v { is_detected_exact<ExpectedType, Operation, Arguments...>{} }
 instance of is_detected_exact<...>::value More...
 
template<typename ConvertibleToType , template< typename... > typename Operation, typename... Arguments>
constexpr auto const iffl::mpl::is_detected_convertible_v { is_detected_convertible<ConvertibleToType, Operation, Arguments...>{} }
 instance of the is_detected_convertible More...
 

Detailed Description

This module contains few meta-programming facilities that are not part of the STL.

Author
Vladimir Petter

iffl github

Helpers for detect idiom. See https://www.youtube.com/watch?v=U3jGdnRL3KI It can be used to safely deduce if an expression is valid for a type. Most likely can be replaced with concepts once they are officially part of the language

//
// To demonstrate use cases of detect idiom we will define 2 classes
//
// This class has only foo, and does not have bar
//
class test1 {
public:
long long foo() {
printf("called test1::foo()\n");
return 111;
}
};
//
// This class has only bar, and does not have foo
// it also defines a nested typedef for difference_type
//
class test2 {
public:
long long boo() {
printf("called test2::boo()\n");
return 112;
}
using difference_type = char;
};
//
// A meta-function that we will use to detect if
// T{}.foo() is a valid expression
//
template <typename T>
using has_foo = decltype(std::declval<T &>().foo());
//
// A meta-function that we will use to detect if
// T{}.boo() is a valid expression
//
template <typename T>
using has_boo = decltype(std::declval<T &>().boo());
//
// Use Case 1:
//
// If T{}.foo() is valid then call it
// If T{}.bar() is valid then call it instead.
//
// We are using SFINAE to eliminate candidates that are not possible
// We are using dependent template parameters on the input types, which turns of template type deduction,
// and because of that we split SFINAE portion to *_impl, and we rely on call_if_can
// to deduce parameter type, and explicitly pass it to the *_impl.
//
template <typename T>
auto call_if_can_impl( std::enable_if_t <mpl::is_detected_v<has_foo, T>, T &> t) {
printf("can call foo\n");
return t.foo();
}
template <typename T>
auto call_if_can_impl( std::enable_if_t <mpl::is_detected_v<has_boo, T>, T &> t) {
printf("can call boo\n");
return t.boo();
}
template <typename T>
auto call_if_can( T & t ) {
return call_if_can_impl<T>(t);
}
//
// Use Case 2:
//
// If T{}.foo() is valid then call it
// If T{}.bar() is valid then call it instead.
// It is the same as Use Case 1, but in this case we do
// SFINAE on unnamed default template parameter.
// in this case type of template parameters deduces fine, but
// compiler complains that two functions have same input and out parameters
// so we need to add an artificial default initialized parameter
// std::integral_constant<int, ?> to make two signatures slightly different.
//
template <typename T,
typename = std::enable_if_t <mpl::is_detected_v<has_foo, T>, void>>
auto call_if_can2(T & t, std::integral_constant<int, 1> const = {}) {
printf("can call 2 foo\n");
return t.foo();
}
template <typename T,
typename = std::enable_if_t <mpl::is_detected_v<has_boo, T>, void>>
auto call_if_can2(T & t, std::integral_constant<int, 2> const = {}) {
printf("can call 2 boo\n");
return t.boo();
}
//
// Use Case 3:
//
// If T::difference type is defined then use it, otherwise
// use std::ptrdiff_t.
//
template<class T>
using diff_t = typename T::difference_type;
template <class T>
using difference_type = mpl::detected_or_t<std::ptrdiff_t, diff_t, T>;
static_assert(std::is_same_v<std::ptrdiff_t, difference_type<test1>>, "test1 does not have difference_type");
static_assert(std::is_same_v<char, difference_type<test2>>, "test2 overwrites difference_type ");
void test_detect() {
test1 t1;
test2 t2;
//
// Use Case 4:
//
// Use for static asserts
// Assert that test1{}.foo() is valid, and test1{}.boo() is not valid
//
static_assert(mpl::is_detected_v<has_foo, test1>, "test1 must have foo()");
static_assert(!mpl::is_detected_v<has_boo, test1>, "test1 must not have boo()");
//
// Use Case 5:
//
// Use if constexpr to choose one of code branches.
// In many cases it is easier comparing to SFINAE on functions,
// but it requires C++17
//
if constexpr (mpl::is_detected<has_foo, test1>{}) {
printf("test1 has foo\n");
t1.foo();
} else {
printf("test1 has no foo\n");
}
if constexpr (mpl::is_detected<has_boo, test1>{}) {
printf("test1 has boo\n");
} else {
printf("test1 has no boo, calling foo instead\n");
t1.foo();
}
//
// Test Use Case 1
//
call_if_can(t1);
call_if_can(t2);
//
// Test Use Case 2
//
call_if_can2(t1);
call_if_can2(t2);
}