Thread: Concept implementation

  1. #1
    Registered User
    Join Date
    Jun 2014
    Posts
    66

    Concept implementation

    Hey folks,

    I'm still playing around with template metaprogramming or more specifically with something that is being referred to as Concepts Lite.

    The goal is to have code such as shown below in the end:
    Code:
    // this function will only be available for incrementable types
    template<typename Type>
    auto increment(Type& value) -> incrementable<Type>; // no valid code at this point
    To implement this, I first did some research and found a reference for `std::void_t' on cppreference.com. It suggests the following definition of `std::void_t' for pre-C++17 compilers:
    Code:
    template<typename... Ts> struct make_void { typedef void type;};
    template<typename... Ts> using void_t = typename make_void<Ts...>::type;
    And it provides an example of expression checking using this type:
    Code:
    // primary template handles types that do not support pre-increment:
    template< class, class = std::void_t<> >
    struct has_pre_increment_member : std::false_type { };
    // specialization recognizes types that do support pre-increment:
    template< class T >
    struct has_pre_increment_member<T,
               std::void_t<decltype( ++std::declval<T&>() )>
           > : std::true_type { };
    Given this code, it's relatively easy to do the following:
    Code:
    int main()
    {
        struct test {
            // test& operator++(); // uncomment to change output
        };
        
        std::cout << has_pre_increment_member<int>() << std::endl; // prints 1
        std::cout << has_pre_increment_member<const int>() << std::endl; // prints 0
        std::cout << has_pre_increment_member<test>() << std::endl; // prints 0 or 1 if `operator++()' is uncommented
    }
    So far, so good. Since having the need of two trait classes for each concept is a bit awkward, I tried a solution based on the above found on StackOverflow. I'll just copy & paste the code found there:
    Code:
    struct Incrementable
    {
        template<class T>
        auto requires_(T&& x)->decltype(++x);
    };
    
    
    template<class Concept,class Enable=void>
    struct models
    : std::false_type
    {};
    
    template<class Concept,class... Ts>
    struct models<Concept(Ts...),void_t< 
        decltype(std::declval<Concept>().requires_(std::declval<Ts>()...))
    >>
    : std::true_type
    {};
    


    This approach looks quite nice, but has a serious problem: it doesn't work properly.
    Code:
    int main()
    {
        struct test {
            // test& operator++(); // uncomment to change output
        };
        
        std::cout << models<Incrementable(int)>() << std::endl; // prints 1
        std::cout << models<Incrementable(const int)>() << std::endl; // prints 1
        std::cout << models<Incrementable(test)>() << std::endl; // prints 0 or 1 if `operator++()' is uncommented
    }


    As you can see, the `models' check still returns true for `const int' as incrementable type, even though `++(const int)' is invalid C++ code. I don't know exactly what's going on here, but it seems to me like some type information is getting lost during the checking process. I played around a bit with perfect forwarding in the `Incrementable' class, but didn't succeed very much.

    Is there anyone who has already dealt with this new use case of template programming? I would be glad if someone could point the problem out, thanks!

    For further reading, there is also a blog post from Eric Niebler and the implementation of his concepts library.

  2. #2
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    Is there anyone who has already dealt with this new use case of template programming?
    O_o

    Code:
    std::cout << models<Incrementable(const int &)>() << std::endl;
    Your problem has literally nothing to do with template programming. Unfortunately, your problem is you don't know enough about the sins of C++ yet to be writing the kind of code you keep trying to write. I'd advise you to slow down. You need to give yourself more time to play with the type system before you try to write tools with so much dependency on having an extremely detailed understanding of the type system. The simple fact is that if you had spent more time playing with function declarations and definitions before all the nesting and indirection associated with metaprogramming you'd have known the issues in both this thread and the last thread you created.

    I'll show you a trivial example as explanation.

    Code:
    void Check1(int){}
    void Check2(const int){}
    
    int main()
    {
        void (*s1)(int);
        void (*s2)(const int);
        s1 = Check1;
        s1 = Check2;
        s2 = Check1;
        s2 = Check2;
        return(0);
    }
    Yeah. I'll simplify for the sake of my time today: the compiler just doesn't care about the `const` on a value parameter in the context of a declaration.

    Seriously, you need to build a thorough foundation before trying so much complicated code. I gave you an elaborate explanation in your last thread, and I hope the explanations sticks to the point that you recognize the same problem wearing different pajamas, but you aren't going to recognize the issues with complicated code in the future if you don't understand the point of failure.

    Soma
    “Salem Was Wrong!” -- Pedant Necromancer
    “Four isn't random!” -- Gibbering Mouther

  3. #3
    Registered User
    Join Date
    Jun 2014
    Posts
    66
    Seriously, you need to build a thorough foundation before trying so much complicated code. I gave you an elaborate explanation in your last thread, and I hope the explanations sticks to the point that you recognize the same problem wearing different pajamas, but you aren't going to recognize the issues with complicated code in the future if you don't understand the point of failure.
    Hm, I guess you have a point... I'll consider to slow down a little bit in the future, sorry

    By the way, for the ones interested in the problem: I managed to solve it. I'll post the code below for reference.

    First thing to do was to define some concept traits:
    Code:
    struct concept {
    };
    
    
    template<typename Type>
    struct incrementable: public concept {
        template<typename T=Type>
        auto operator()() -> decltype(++std::declval<T&>());
    };
    
    
    template<typename First, typename Second=First>
    struct comparable: public concept {
        template<typename F=First, typename S=Second>
        auto operator()() -> decltype(std::declval<F&>()==std::declval<S&>());
    };
    Next, a feature that allows one to convert them into a compile-time boolean:
    Code:
    template<typename, typename=void>
    struct model: public std::false_type {
    };
    
    
    template<typename Concept>
    struct model<Concept, void_t<decltype(Concept{}())>>: public std::true_type {
    };
    And last, some functionality to achieve the intended look of concept handling:
    Code:
    template<typename First, typename Second=void, typename... Types>
    struct check {
        using type=typename std::enable_if<model<First>::value,
            typename check<Second, Types...>::type>::type;
    };
    
    
    template<typename Type>
    struct check<Type,
        typename std::enable_if<std::is_base_of<concept, Type>::value>::type> {
        using type=typename std::enable_if<model<Type>::value>::type;
    };
    
    
    template<typename Type>
    struct check<Type,
        typename std::enable_if<!std::is_base_of<concept, Type>::value>::type> {
        using type=Type;
    };
    
    
    template<typename... Types>
    using trait=typename check<Types...>::type;
    Well, honestly I didn't think it would be that little code, but as it works I'm satisfied. One can use it the following way:
    Code:
    template<typename Type>
    auto increment(Type& value) -> trait<incrementable<Type>> // evaluates to void
    {
        ++value;
    }
    
    
    template<typename First, typename Second=First>
    auto compare(const First& left, const Second& right)
        -> trait<comparable<First, Second>, bool> // evaluates to bool
    {
        return (left==right);
    }
    
    
    template<typename First, typename Second=First>
    auto increment_and_compare(First& left, const Second& right)
        -> trait<incrementable<First>, comparable<First, Second>, bool> // evaluates to bool
    {
        ++left;
        return (left==right);
    }
    
    
    int main()
    {
        std::cout << model<incrementable<int>>() << std::endl; // prints 1
        std::cout << model<incrementable<const int>>() << std::endl; // prints 0
        
        std::cout << model<comparable<int, const int>>() << std::endl; // prints 1
        std::cout << model<comparable<int*, const int>>() << std::endl; // prints 0
        
        {
            auto value=0;
            increment(value); // fine
        }
        
        {
            const auto value=0;
            increment(value); // error
        }
        
        increment(33); // error
        
        std::cout << compare(true, false) << std::endl; // prints 0
        std::cout << compare('a', 'a') << std::endl; // prints 1
        std::cout << compare('a', "abc") << std::endl; // error
        
        {
            auto first=0, second=1;
            std::cout << increment_and_compare(first, second) << std::endl; // prints 1
        }
        
        {
            const auto first=0, second=1;
            std::cout << increment_and_compare(first, second) << std::endl; // error
        }
    }
    Well, that's it. Thanks for your assistance!
    Last edited by milli-961227; 07-17-2015 at 03:36 PM.

  4. #4
    [](){}(); manasij7479's Avatar
    Join Date
    Feb 2011
    Location
    *nullptr
    Posts
    2,657
    Here is an excellent video from CppCon 2014 by the guy who 'discovered' the void_t trick.
    He also goes over many other meta programming tricks you may or may not know.

    Part 1 https://www.youtube.com/watch?v=Am2is2QCvxY
    Part 2 https://www.youtube.com/watch?v=a0FliKwcwXE

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Can someone explain this concept to me
    By sigur47 in forum C++ Programming
    Replies: 4
    Last Post: 03-12-2012, 07:35 AM
  2. Replies: 7
    Last Post: 10-01-2008, 07:45 PM
  3. Concept
    By kusal in forum C Programming
    Replies: 12
    Last Post: 01-06-2007, 10:32 PM
  4. new to Templates' concept
    By Brain Cell in forum C++ Programming
    Replies: 6
    Last Post: 03-07-2005, 04:07 AM
  5. Concept help
    By Mithoric in forum Windows Programming
    Replies: 13
    Last Post: 04-18-2004, 03:05 PM