2

This question relates to an earlier one I asked regarding implementing something akin to Qt's signal/slots in C++11.

Consider the following (very simplified signal dispatcher, that in this example does nothing of any use, it's just to demonstrate the pattern/problem):

template< typename... TYPES >
class Signal
{
public:
    Signal()  = default;
    ~Signal() = default;

    template< typename... PARAMETERS >
    void broadcast( PARAMETERS &&... p )
    {
        // static_assert to confirm PARAMETERS can map to TYPES
    }
};

This works well enough, but there's some unwanted type conversion going on in practice. e.g.;

// acceptable use.
Signal< int, unsigned, float, char >().broadcast(   1, 2u, 0.f, 'a' );

// should fail compilation, first parameter is a float, 4th is an int.
Signal< int, unsigned, float, char >().broadcast( 0.f,  0, 0.f,   0 );

// acceptable use, first parameter is const, but it's convertible.
const int   i  = 3;
Signal< int, unsigned, float, char >().broadcast(  i, 2u, 0.f, 'a');

// acceptable use, first parameter is const &, but it's convertible.
const int & j = i;
Signal< int, unsigned, float, char >().broadcast(  j, 2u, 0.f, 'a');

There should be no silent float to int conversion. Conversion of const/const & in this instance should be possible (the format of TYPES should not have const or & as all data should be passed by value).

I'd like to prevent compilation where such unwanted type conversion happens. I thought to wrap up both TYPES and PARAMETERS in tuples, iterate over the tuple and confirm that each type in a given tuple parameter index matches (including using std::decay), but then I couldn't see a way to do that at compile time so that it could go in a static_assert.

For reference, compilers of choice are clang (latest on OS X 7.3 (clang-703.0.31)) and vc14.

Is what I want to do possible and, if so, can anyone offer any pointers?

2 Answers2

4

Using (once again) the all_true bool pack trick from Columbo:

template <bool...> struct bool_pack;
template <bool... v>
using all_true = std::is_same<bool_pack<true, v...>, bool_pack<v..., true>>;

template <class... Args>
struct Signal {
    template <class... Dargs, class = typename std::enable_if<all_true<
        std::is_same<Args, typename std::decay<Dargs>::type>{}...
    >{}>::type>
    void broadcast(Dargs &&...) {}
};

This SFINAE's away the function if the parameters don't match exactly.

Community
  • 1
  • 1
Quentin
  • 62,093
  • 7
  • 131
  • 191
  • Hmm, does not seem to work in vc14. Using `Signal< int, float, char > Test; Test.broadcast( 1, 1.f, 'a' );` vc14 complains thusly; `1>C:\Development\tinkering\source\main.cpp(28): error C2783: 'void Signal::broadcast(Dargs &&...)': could not deduce template argument for '' 1> C:\Development\tinkering\source\main.cpp(20): note: see declaration of 'Signal::broadcast' ` - Not in a position to give clang a try until tomorrow. – Slartibartfast May 26 '16 at 20:58
  • @Slartibartfast I guess that's another instance of MSVC's broken SFINAE... As a workaround you can `static_assert(all_true<...>, "");` inside the function. – Quentin May 26 '16 at 21:12
  • Maybe I misunderstood the workaround, but I still couldn't get vc14 to swallow it. It does work as required on clang. Shame as this looks to be a better solution than the one suggested by @Arunmu , however the other solution seems to work on the two compilers I currently use without fuss so in this instance I'd lean toward the other answer. – Slartibartfast May 27 '16 at 07:00
  • @Slartibartfast I have been unclear indeed. I'm suggesting that you remove the `, class = std::enable_if<...>` from the template parameters, and instead `static_assert` its content inside the function. – Quentin May 27 '16 at 07:42
3

Here is a metaprogram I quickly came up with. It is a bit coarse, but can be implemented in a more better way. You should probably use the decayed type (std::decay) in the metaprogram to get correct result.

#include <iostream>
#include <type_traits>

template <typename... T> struct param_pack {};

template <typename, typename> struct is_all_same_impl;

template <>
struct is_all_same_impl<param_pack<>, param_pack<>>
{
  static bool const value = true;
};

template <typename T, typename S, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<S, SRest...>>
{
  static bool const value = false;
};

template <typename T, typename... Rest, typename... SRest>
struct is_all_same_impl<param_pack<T, Rest...>, param_pack<T, SRest...>>
{
  static bool const value = is_all_same_impl<param_pack<Rest...>, param_pack<SRest...>>::value;
};

template <typename, typename>
struct is_all_same;

template <typename... FSet, typename... SSet>
struct is_all_same<param_pack<FSet...>, param_pack<SSet...>>: is_all_same_impl<param_pack<FSet...>, param_pack<SSet...>> {};

int main() {
  std::cout << is_all_same<param_pack<int, char, float>, param_pack<int, char, int>>::value << std::endl;
  return 0;
}

UPDATE :: More simpler version

template <typename... T> struct param_pack {};

int main() {
  std::cout << std::is_same<param_pack<int, float, int>, param_pack<int,float,int>>::value << std::endl;
  return 0;
}

So you can do something like:

static_assert( is_same<param_pack<Args...>, param_pack<std::decay_t<Dargs>...>>::value, "Parameters do not sufficiently match." );
Arunmu
  • 6,837
  • 1
  • 24
  • 46
  • Seems to work fine on my rough tests so far under vc14 at least, although I couldn't seem to get std::decay into the metaprogram, although putting it in the static_assert *seems* to work; `static_assert( is_all_same, param_pack...>>::value, "Parameters do not sufficiently match." );` – Slartibartfast May 26 '16 at 21:32
  • @Slartibartfast Yeah, thats what I meant. Put std::decay while calling is_all_same.Sorry for not being clear, was in some sort of hurry while replying :) – Arunmu May 26 '16 at 21:35
  • Would you mind elaborating on how/why this works? I can see that `value` is defined in 3 possible places, but I'm puzzled how each one is picked, especially as some of the is_all_same_impl implementations take far more template parameters that I'd expect. – Slartibartfast May 27 '16 at 07:08
  • @Slartibartfast My first solution is definitely over engineered. Check my updated answer..lot simpler :) – Arunmu May 27 '16 at 07:11