none
Selecting types with some properties from typelist RRS feed

  • Question

  • It is VS 2019 Community Update 15.6.9 VC++ Project under Windows 10 Home.

    I have a typelist of types some of them have some property, and others have not. I want to select types that have the property into different typelist. I have a solution for the some property as a partial specialization, but I can't get it in generic form.

    So, first let get template for a typelist:

    template <typename... Ts>
    struct tlist
    {
      using type = tlist;
      static constexpr size_t size() noexcept { return sizeof...(Ts); }
    };
    

    And there are templates I am usiing:

    template <typename Head, typename... Ts>
    struct tlist_front;
    
    template <typename Head, typename... Ts>
    struct tlist_front<tlist<Head, Ts...>>
    {
      using type = Head;
    };
    
    template <size_t idx, class List>
    struct tlist_type_at_impl;
    
    template <typename T, typename... Ts>  ///> End of search, type is found
    struct tlist_type_at_impl < 0, tlist<T, Ts...> >
    {
      using type = T;
    };
    
    /// Recursion
    template <size_t idx, typename T, typename...Ts>
    struct tlist_type_at_impl <idx, tlist<T, Ts...>>
    {
      using type = typename tlist_type_at_impl<idx - 1, tlist<Ts...>>::type;
    };
    
    /// Wrapper
    template <size_t idx, class List>
    struct tlist_type_at;
    
    template <size_t idx, typename... Ts>
    struct tlist_type_at<idx, tlist<Ts...>>
    {
    private:
      static_assert(sizeof...(Ts) > idx,
        "tlist_type_at: Index out of bounds or called on empty list");
    public:
      using type = typename tlist_type_at_impl<idx, tlist<Ts...>>::type;
    
    };
    
    
    template <class List>
    struct tlist_back;
    
    template <typename... Ts>
    struct tlist_back<tlist<Ts...>>
    {
    private:
      static_assert(sizeof...(Ts) > 0, "tlist_back: Called on empty list");
    public:
      using type = typename tlist_type_at< (sizeof...(Ts)) - 1, tlist<Ts...>>::type;
    };
    
    /// Pop front, this can be called on empty list
    
    template <typename Head, typename... Ts>
    struct tlist_pop_front;
    
    template <typename Head, typename...  Ts>
    struct tlist_pop_front<tlist<Head, Ts...>>
    {
      using type = typename tlist<Ts... > ;
    };
    

    All that was published in my artoc;e on CodeProject.

    Now, types included in the initial typelist:

    template <typename LTP>
    struct Layer;
    
    template <>
    struct Layer<LT0> : public LT0
    {
      static inline const bool bHasWeights =  false;
      static inline const bool bHasNeurons =  false;
    
     //……………………………………………………………………………………………………………….
    };
    
    using L0 = Layer<LT0>;
    
    template <>
    struct Layer<LT1> : public LT1
    {
      static inline const bool bHasWeights = true;
      static inline const bool bHasNeurons =  false;
    
    //………………………………………………………………………………………………………..
    };
    
    using L1 = Layer<LT1>;
    
    template <>
    struct Layer<LT2> : public LT2
    {
      static inline const bool bHasWeights = false;
      static inline const bool bHasNeurons =  true;
    
    //………………………………………………………………………………………………………………...
    
    };
    
    using L2 = Layer<LT2>;
    
    template <>
    struct Layer<LT3> : public LT3
    {
      static inline const bool bHasWeights = true;
      static inline const bool bHasNeurons =  false;
    
    //……………………………………………………………………………………………………………...
    
    };
    
    using L3 = Layer<LT3>;
    
    using MyList = tlist<L0, L1, L2, L3;
    
    

    Now, the selection template:

    template <bool bFl, class LSrc, class LRes>
    struct all_with_fn;
    
    template <class L, class... Rs>
    struct all_with_fn <true, tlist<L>, tlist<Rs...>>
    {
      using type = tlist < Rs..., L>;
    };
    
    template <bool bFl, class L, class... Rs>
    struct all_with_fn<bFl, tlist<L>,tlist<Rs...>>
    {  
      using type = tlist < Rs... > ;
    };
    
    template <class...Ls, class... Rs>
    struct all_with_fn<true, tlist<Ls...>, tlist<Rs...>>
    {
      using LCurr = typename tlist_front<tlist<Ls...>>::type;
      using resRs = tlist<Rs..., LCurr>;
      using nextLs = typename tlist_pop_front<tlist<Ls...>>::type;
      using nextL = typename tlist_front<nextLs>::type;
    
      using type = typename all_with_fn < nextL::m_bHasWeights, nextLs, resRs>::type;;
    };
    
    
    template <bool bFl, class...Ls, class... Rs>
    struct all_with_fn<bFl, tlist<Ls...>, tlist<Rs...>>
    {
      using resRs = tlist<Rs...>;
      using nextLs = typename tlist_pop_front<tlist<Ls...>>::type;
      using nextL = typename tlist_front<nextLs>::type;
    
      using type = typename all_with_fn < nextL::m_bHasWeights, nextLs, resRs>::type;;
    };
    
    using MyAllLs = tlist<L0, L1, L2, L3>;
    using LWW = tlist<>;
    using LCurr = tlist_front<MyAllLs>::type;
    using MyLstWithWeiights = all_with_fn < L0::m_bHasWeights, tlist<L2>, tlist<>>::type;
    
    

    In main function

    int main()
    {
      using resL1 = all_with_fn<L0::m_bHasWeights, tlist<L0, L1, L2, L3>, tlist<>>::type;
      size_t wLSz = resL1::size();
    
    //......................................
    }

    You can see this is specialized for property bHasWeights. It works just fine. I can specialize it for any other bool property of members of initial typelist. but what if you have , say, ten properties?

    I feel that should be some constexpr template function, like 

    template <class L?
    constexpr bool GetProp(){return L:::m_b...;}

    which I can pass to template, but I do not know haw to do that.

    Any help will greatly appreciated.

     

    Saturday, May 23, 2020 9:25 PM

Answers

  • I think you are making it way too complicated. Something like this should work:

    template <template <typename> class F, typename GoodList, typename RestList>
    struct all_with_fn_impl;
    
    template <template <typename> class F, typename GoodList>
    struct all_with_fn_impl<F, GoodList, tlist<>> {
      using type = GoodList;
    };
    
    template <template <typename> class F, typename Head, typename... Tail, typename... Good>
    struct all_with_fn_impl<F, tlist<Good...>, tlist<Head, Tail...>> {
      using type = typename std::conditional_t<
        F<Head>::value,
        all_with_fn_impl<F, tlist<Good..., Head>, tlist<Tail...>>,
        all_with_fn_impl<F, tlist<Good...>, tlist<Tail...>>
      >::type;
    };
    
    template <template <typename> class F, typename AllList>
    struct all_with_fn {
      using type = typename all_with_fn_impl<F, tlist<>, AllList>::type;
    };

    Demo: https://godbolt.org/z/w9Zzbj



    Igor Tandetnik



    • Edited by Igor Tandetnik Tuesday, May 26, 2020 2:45 PM
    • Marked as answer by Geoyar Tuesday, May 26, 2020 11:10 PM
    Tuesday, May 26, 2020 2:39 PM

All replies

  • Unless I am misunderstanding what you are trying to do entirely, there is no way to currently get the compiler to work on any kind of type information. This means that there is no way to say to the compiler, I want to work on the second member of struct s.

    To generalise this more you would need static reflection and unfortunately this is currently slated for C++23. If my understanding of this proposal is right then you can use this to do something like:

    struct s
    {
    	bool b1;
    	bool b2;
    	bool b3;
    };
    
    template<typename _T>
    constexpr bool get_member_value(_T my_t, size_t member)
    {
    	namespace reflect = std::experimental::reflect;
    	using refl_t = reflexpr(my_t);
    	return get_constant_v<get_element<member, get_data_members_t<refl_t>>>;
    }
    
    constexpr bool g_v = get_member_value(s{true, false, true}, 1);

    If you want to work on static values then I'm sure that:

    struct s
    {
    	static constexpr inline bool b1 = true;
    	static constexpr inline bool b2 = false;
    	static constexpr inline bool b3 = true;
    };
    
    template<typename _T>
    constexpr bool get_member_value(size_t member)
    {
    	namespace reflect = std::experimental::reflect;
    	using refl_t = reflexpr(_T);
    	return get_constant_v<get_element<member, get_data_members_t<refl_t>>>;
    }
    
    constexpr bool g_v = get_member_value<s>(0);

    should work too.

    However, as I said, this is off in the future.


    This is a signature. Any samples given are not meant to have error checking or show best practices. They are meant to just illustrate a point. I may also give inefficient code or introduce some problems to discourage copy/paste coding. This is because the major point of my posts is to aid in the learning process.


    Sunday, May 24, 2020 1:38 AM
  • You might be looking for a template template parameter:

    template <template <typename U> F, class LSrc, class LRes>
    struct all_with_fn;

    Now, whenever you need to test whether the property is true, use F<SomeType>::value, e.g. F<nextL>::value in place of nextL::m_bHasWeights. A concrete functor may look like this:

    template <typename U>
    struct HasWeights {
      static const bool value = U::m_bHasWeights;
    };


    Igor Tandetnik

    Sunday, May 24, 2020 1:40 AM
  • Thank you very much for replay.

    But for me, might be I am wrong, the problem is not with getting one and same property for all types in the list. The problem is in specialization fr different properties.

    There are different actions for the property. So I must to give the property value when I call the template and inside it, when I unwind to next level. Ok, I am writing specialization for m_bHasWeights, entering it for front type in the call, and for the second type in unwinding call inside the template. So far, so good.

    Next, I want to do the same for property, say, m_bHasChicken. Again, I should write a specialization for that like I did it for m_bHasWeights. And for other properties. Might be 10 separate specializations.

    And I asked if it is possible to write template where I would give only once  a property as a template argument..

    Sunday, May 24, 2020 10:51 PM
  • With my approach, you don't need to write all_with_fn specialization for each property. You only need to write an adapter similar to HasWeights, and pass that to all_with_fn.

    all_with_fn implementation wouldn't directly mention, say, m_bHasWeights anyhwhere.

    Is this not what you want?


    Igor Tandetnik

    Monday, May 25, 2020 12:38 AM
  • As I understand it, it is exactly what I want. To write template template  meta class, and specialize it according to application needs.

    I have problems with details.

    The initial template, as I understand, should be

    template <bool bFlag, typename...Ls, typename... Rs>
    struct all_with_fn; 

    According to bool value it adds the head of source files to result or do not and goes to next source type.

    The partial specialization of it is template template thing like

    template <template<> typenanme<F>, typename L, typename... Ls, typename>>> Rs>
    struct all_with_fn<F<L>::value, tlist<L, Ls...>, tlist<Rs...>>
    {'''};

    F<l>::value is supposed to be of bool type.

    But there is no info about the type of F<L>::value, that it should exist, and the compiler complains that it could be of any type and it cannot deduce the type of the value.

    I know, way to guarantee the type of value is by using a functor template class or pointer to function returning bool. But there is a problem to execute functor's operator () or function in compile time, before instantiation of template. I do not know is there soething else.

    The second complaint I do not quire understand, but there is a way around At the end of unwinding chain:

    template <class L, class... Rs>
    struct all_with_fn <true, tlist<L>, tlist<Rs...>>
    {
      using type = tlist < Rs..., L>;
    };
    
    template <template <> typename F, class L, class... Rs>
    struct all_with_fn<F<L>::value, tlist<L>,tlist<Rs...>>
    {  
      using type = tlist < Rs... > ;
    };

    the compiler says that F is not used in template. but it accepts

    template <class L, class... Rs>
    struct all_with_fn <true, tlist<L>, tlist<Rs...>>
    {
      using type = tlist < Rs..., L>;
    };
    
    template <class L, class... Rs>
    struct all_with_fn<false, tlist<L>,tlist<Rs...>>
    {  
      using type = tlist < Rs... > ;
    };
    Because it is the end of chain, there is no farther need for class F, and it works.

    The project is set with last version of Win 10 SDK, platform toolchain VS 2019 (v 142) and ISO C++17 Standard.


    • Edited by Geoyar Monday, May 25, 2020 8:27 PM Grammar
    Monday, May 25, 2020 8:16 PM
  • I think you are making it way too complicated. Something like this should work:

    template <template <typename> class F, typename GoodList, typename RestList>
    struct all_with_fn_impl;
    
    template <template <typename> class F, typename GoodList>
    struct all_with_fn_impl<F, GoodList, tlist<>> {
      using type = GoodList;
    };
    
    template <template <typename> class F, typename Head, typename... Tail, typename... Good>
    struct all_with_fn_impl<F, tlist<Good...>, tlist<Head, Tail...>> {
      using type = typename std::conditional_t<
        F<Head>::value,
        all_with_fn_impl<F, tlist<Good..., Head>, tlist<Tail...>>,
        all_with_fn_impl<F, tlist<Good...>, tlist<Tail...>>
      >::type;
    };
    
    template <template <typename> class F, typename AllList>
    struct all_with_fn {
      using type = typename all_with_fn_impl<F, tlist<>, AllList>::type;
    };

    Demo: https://godbolt.org/z/w9Zzbj



    Igor Tandetnik



    • Edited by Igor Tandetnik Tuesday, May 26, 2020 2:45 PM
    • Marked as answer by Geoyar Tuesday, May 26, 2020 11:10 PM
    Tuesday, May 26, 2020 2:39 PM
  • It works. Thank you. Marked as answer. Never used std::conditional.
    Tuesday, May 26, 2020 11:10 PM