How to use the universal template function to process objects with different members?

I was looking a bit to solve this issue, however, perhaps I do not know the exact definition or syntax of the language of what I'm trying to accomplish, so I decided to publish.

I have certain objects / structures like:

struct A
{
  char myChar;
  bool hasArray = false;
};

template <uint8_t ARRAY_LEN>
struct AA : public A
{
  hasArray = true;
  uint8_t myArray[ARRAY_LEN];
};

I want to create a general function that can take in both of these types of objects, and do the general work, as well as the specific work for the derivative struct AA. Something like the following:

template <typename T>
void func(T (&m)) 
{
  if (T.hasArray)
  {
    // do some processing with m.myArray
    std::cout << sizeof(m.myArray) << std::endl;
    // ...
  }
  // common processing
  std::cout << "myChar: " << m.myChar << std::endl;
};

I want to be able to call a function as follows:

A a;
AA aa;
func(a);   // compiler error, this would not work as no array member
func(aa);  // this works

, , , , . . , , , ? , , , , ++, . ... - ++ 11 GCC 4.8.5

+4
3

, :

#include<iostream>
#include<cstdint>

struct A
{
    char myChar;
};

template <uint8_t ARRAY_LEN>
struct AA : public A
{
    uint8_t myArray[ARRAY_LEN];
};

void func(const A &m)
{
    std::cout << "myChar: " << m.myChar << std::endl;
};

template <uint8_t AL>
void func(const AA<AL> &m) 
{
    std::cout << sizeof(m.myArray) << std::endl;
    func(static_cast<const A &>(m));
}

int main() {
    func(A{});
    func(AA<1>{});
}

sfinae, , , - :

#include<iostream>
#include<cstdint>

struct A
{
    char myChar;
};

template <uint8_t ARRAY_LEN>
struct AA : public A
{
    uint8_t myArray[ARRAY_LEN];
};

void func(A &m)
{
    std::cout << "myChar: " << m.myChar << std::endl;
}

template <typename T>
auto func(T &m) -> decltype(m.myArray, void())
{
    std::cout << sizeof(m.myArray) << std::endl;
    A &a = m;
    func(a);
}

int main() {
    AA<1> aa{};
    A a{};
    func(a);
    func(aa);
}

, hasArray.

+1

++ 14 . ++ 17 if constexpr, ; .

template<std::size_t I>
using index_t=std::integral_constant<std::size_t, I>;
template<std::size_t I>
constexpr index_t<I> index{};

constexpr inline index_t<0> dispatch_index() { return {}; }
template<class B0, class...Bs,
  std::enable_if_t<B0::value, int> =0
>
constexpr index_t<0> dispatch_index( B0, Bs... ) { return {}; }
template<class B0, class...Bs,
  std::enable_if_t<!B0::value, int> =0
>
constexpr auto dispatch_index( B0, Bs... ) { 
  return index< 1 + dispatch_index( decltype(Bs){}...) >;
}

template<class...Bs>
auto dispatch( Bs... ) {
  using I = decltype(dispatch_index( decltype(Bs){}... ));
  return [](auto&&...args)->decltype(auto){
    return std::get<I::value>( std::make_tuple(decltype(args)(args)..., [](auto&&...){}) );
  };
}

dispatch( some_test ) lambda, auto&&.... , , , some_test true-like-type, ( [](auto&&...){}, ), some_test false-like.

myArray.

namespace details {
  template<template<class...>class Z, class=void, class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z, class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = typename details::can_apply<Z, void, Ts...>::type;

template<class T>
using myArray_type = decltype( std::declval<T>().myArray );

template<class T>
using has_myArray = can_apply< myArray_type, T >;

has_myArray<T> , T .myArray.

dispatch( has_myArray<T>{} )(
  [&](auto&& m) {
    // do some processing with m.myArray
    std::cout << sizeof(m.myArray) << std::endl;
    // ...
  }
)( m );

, m.myArray.

, , , , , , .

non-++ 11, MSVC 2015,

std::enable_if_t<B0::value, int> =0

std::enable_if_t<!B0::value, int> =0

class = std::enable_if_t<B0::value>

class = std::enable_if_t<!B0::value>, class=void

. , . MSVC.

++ 14, void_t enable_if_t, , enable_if.

, index ++ 11. index<blah> index_t<blah>{}.

auto&& lambdas ; , , . , auto lambdas, ++ 14 , , ++ 11.

, .

+2

?

, sizeof(m.myArray) , myArray. , , .

, , hasArray , myArray . , static constexpr, .

struct A
 {
   static constexpr bool hasArray { false };

   char myChar { 'z' };
 };

template <uint8_t ARRAY_LEN>
struct AA : public A
 {
   static constexpr bool hasArray { true };

   uint8_t myArray[ARRAY_LEN];
 };

Now, in func(), you can call the second function func2()to select two cases: myArrayor not myArray. You can use SFINAE for this, but (IMHO) best sends the tag in this case. So you can convert yours hasArrayto another type

template <typename T>
void func2 (T const & m, std::true_type const &)
 { std::cout << sizeof(m.myArray) << ", "; }

template <typename T>
void func2 (T const &, std::false_type const &)
 { }

template <typename T>
void func(T (&m)) 
 {
   func2(m, std::integral_constant<bool, T::hasArray>{});

   // common processing
   std::cout << "myChar: " << m.myChar << std::endl;
 }

Now you can call func()with both types

int main()
 {
   A       a;
   AA<12U> aa;

   func(a);  // print myChar: z
   func(aa); // print 12, myChar: z
 }

Remember to include type_traitsand iostream.

+1
source

Source: https://habr.com/ru/post/1668311/


All Articles