質問

How can I make a function that accept same numbers of string - int - char

func("aa","sd","zzz",123,13,3,'w','a','x') //correct
func("aa","sd",123,3,'w','x') //correct
func("aa",2,'w') //correct 
func()//correct 
func("aa","VV",23,'w') //error because 2 string 1 int and 1 char
func('a',2) //error 0 string 1 char 1 int  
.....

I want to check variables at compile time not at the run time so I can't use std::vector.

Any Idea ? Is it possible ?

役に立ちましたか?

解決 3

An easy way in C++11 is:

template <std::size_t N>
void func(const std::array<const char*, N>& cstrings,
          const std::array<int, N>& ints
          const std::array<char, N>& chars);

And so you may use it:

func({ "aa","sd","zzz"}, {123, 13, 3} , {'w', 'a', 'x'});

If you want to use the same syntax, it is more verbose and use SFINAE: it falls back to call the version with std::array as it is more convenient to implement

#include <cstdint>
#include <array>
#include <tuple>
#include <type_traits>

#if 1 // Not in C++11

template <std::size_t ...> struct index_sequence {};

template <std::size_t I, std::size_t ...Is>
struct make_index_sequence : make_index_sequence < I - 1, I - 1, Is... > {};

template <std::size_t ... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> {
    typedef index_sequence<Is...> type;
};

#endif

template <std::size_t N>
void func(const std::array<const char*, N>& cstrings,
          const std::array<int, N>& ints
          const std::array<char, N>& chars)
{
    // Add your code here
}

namespace detail
{
    template <typename T, typename SEQ, typename SEQ2> struct helper_split3;

    template <typename ... Ts, std::size_t ... Is, std::size_t ... Is2>
    struct helper_split3<std::tuple<Ts...>, index_sequence<Is...>, index_sequence<Is2...> >
    {
        typedef std::tuple<typename std::tuple_element<Is, std::tuple<Ts...> >::type...> First;
        typedef std::tuple<typename std::tuple_element<sizeof...(Is) + Is, std::tuple<Ts...> >::type...> Middle;
        typedef std::tuple<typename std::tuple_element<2 * sizeof...(Is) + Is2, std::tuple<Ts...> >::type...> Last;
    };

    template <typename T, typename ... Ts> struct are_same : std::true_type {};

    template <typename T, typename Tail, typename ... Ts>
    struct are_same<T, Tail, Ts...> :
        std::conditional<std::is_same<T, Tail>::value && are_same<T, Ts...>::value,
                        std::true_type, std::false_type>::type {};

    template <typename T, typename Tuple> struct are_same_in_tuple;

    template <typename T, typename ... Ts>
    struct are_same_in_tuple<T, std::tuple<Ts...>> : are_same<T, Ts...> {};

    template<typename ... Ts>
    struct func_trait
    {
        typedef helper_split3<std::tuple<Ts...>,
                typename make_index_sequence<sizeof...(Ts) / 3>::type,
                typename make_index_sequence<sizeof...(Ts) - 2 * (sizeof...(Ts) / 3)>::type>
                helper;

        static const bool value = (sizeof...(Ts) % 3 == 0)
                                  && are_same_in_tuple<const char*, typename helper::First>::value
                                  && are_same_in_tuple<int, typename helper::Middle>::value
                                  && are_same_in_tuple<char, typename helper::Last>::value;
    };

    template <typename ... Ts, std::size_t ... Is>
    void func(std::tuple<Ts...>&& t, index_sequence<Is...>)
    {
        constexpr std::size_t N = sizeof...(Ts);
        ::func<N>(std::array<const char*, N>({std::get<Is>(t)...}),
                  std::array<int, N>({std::get<sizeof...(Is) + Is>(t)...}),
                  std::array<char, N>({std::get<2 * sizeof...(Is) + Is>(t)...}));
    }

} // namespace detail

template <typename ... Ts>
typename std::enable_if<detail::func_trait<Ts...>::value, void>::type
func(Ts...args)
{
    detail::func(std::tie(args...), make_index_sequence<sizeof...(Ts) / 3>());
}

他のヒント

Supposing your parameter looks like this:

struct Param
{
  string mS;
  char mC;
  int mN;
};

Instead of trying to compose a function which takes an unknown or variable number of arguments, have the function take a vector.

void func (const vector <Param>& parameters)

Better yet, have the function take a pair of iterators to said vector:

void func (vector <Param>::const_iterator begin, vector <Param>::const_iterator end)

Better yet still, genericize the container and the iterator:

template <typename Iterator>  void func (Iterator begin, Iterator end)

In the last case, the iterators can refer to anything that conforms to the restrictions of an iterator. This includes C-style arrays. You might use this like this:

void Doer()
{
  Params params[] = 
  {
    Param("aa",123, 'W'),
    Param("sd",13,'a'),
    Param("zzz",3,'x')
  };
  const size_t numParams = sizeof (params) / sizeof (params[0]);
  func (params, params + numParams);
}

Of course you'll need a constructor:

struct Param
{ 
  Param (const std::string s, int n, char c)
  :
    mS (s),
    mN (n),
    mC (c)
  {
  }
// ...
};

And in some cases you might need a maker type function:

Param make_param (const std::string& s, int n, char c)
{
  Param retval (s, n, c);
  return retval;
}

(The question was originally tagged both C and C++; the C tag was later removed. Part of the following is specific to C, but I'm leaving it here in case it's useful to others.)

In C, no, I don't believe it's possible to do this with compile-time checking. Variadic functions (a) require at least one fixed parameter (eliminating your func() example), and (b) do not check the types of the variadic arguments at compile time. Variadic macros (new in C99) might be more promising, but they perform no type checking. I've thought about how to do this with nested generic selections (a new feature in C11, nonexistent in C++), but I don't believe they can solve the problem.

If you're using C (this question was originally tagged both C and C++), I think the closest you can get is to have a distinct function for each number of parameters:

void func0(void);
void func1(char*, int, char);    
void func2(char*, char*, int, int, char, char);
void func3(char*, char*, char*, int, int, int, char, char, char);

You know when you're writing the call how many parameters you're passing, so specifying a different function isn't too much of a burden.

In C++, you could provide multiple overloaded functions, each of which takes arguments of the appropriate types:

void func();
void func(char*, int, char);    
void func(char*, char*, int, int, char, char);
void func(char*, char*, char*, int, int, int, char, char, char);

And so on, for as many overloadings as you have patience for.

Neither of these solutions is indefinitely scalable to an arbitrary number of arguments.

I had thought that there's no general solution in C++, but Jarod42's answer (which I haven't yet taken the time to understand) appears to have shown otherwise. On the other hand, there's something to be said for simplicity. On the other other hand, you only have to write it once.

One more thing: your last example is:

func('a',2) //error 0 string 1 char 1 int 

In C, both 'a' and 2 are of the same type, namely int. In C++, a is of type char. (Again, this isn't necessarily relevant if you're only concerned with C++.)

In C and C++, when writing variadic functions, there are three big rules:

  1. You must always know the size and type of the first parameter.
  2. You must be able to deduce the size and type of the remaining arguments.
  3. You must know when you've reached the last argument.

This is how functions like printf work.
It's first parameter is always a char*.
The next set of parameters are dictated by the %x formatters found.
It knows its out of arguments when it reached the last Percent-specifier.

If you can follow those three rules, any online guide to writing variadic functions will help you accomplish what you're after.

You can't do this the way you ask. Your best bet would be to make a structure type containing those three types and pass in a variable number of those structures.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top