RMHarris157
RMHarris157

Reputation: 279

std::tuple_element<>::type resolution issues

As you can see in the code below, I'm trying to mock up a callback structure supplying std::tuple<> of messages into a factory. My issue is that it looks like the compiler isn't reducing the "return type" of (for example) std::tuple_element< 2, MessageOne, MessageTwo, MessageTen > to just MessageTwo -- which is confusing the linkage. See below.

#include <map>
#include <tuple>

//----------------------------------------------------------------------
//
class MessageBase
{
  public:
    MessageBase( const int _id ) : m_id( _id ) { }
    virtual int getMessageID() const = 0;

  private:
    const int m_id;
};

#define MESSAGE( NAME, VAL ) \
  class Message##NAME : public MessageBase { \
    public: \
      Message##NAME() : MessageBase( VAL ) { } \
      virtual int getMessageID() const { return ( VAL ); } \
  }

  MESSAGE( One, 1 );
  MESSAGE( Two, 2 );
  MESSAGE( Ten, 10 );

class StaticMessageHandlerInterface
{
  public:

    virtual void processMsg( const MessageOne& ) { }
    virtual void processMsg( const MessageTwo& ) { }
    virtual void processMsg( const MessageTen& ) { }
};


//----------------------------------------------------------------------
//
class MessageCallbackInterface
{
  public:
    virtual void receiveMsg( const MessageBase* ) = 0;

};

template< class MessageT, class HandlerT >
class MessageCallbackHandler : public MessageCallbackInterface
{
  public:
    MessageCallbackHandler( HandlerT& _handler )
      : r_handler( _handler )
    { }

    void receiveMsg( const MessageBase* _msg )
    {
      const MessageT* msg( ( const MessageT* )(_msg) );
      r_handler.processMsg( *msg );
    }

  private:
    HandlerT& r_handler;

}; // MessageCallbackHandler


//----------------------------------------------------------------------
//
template< typename TupleT >
class MessageFactory
{
  public:
    MessageFactory()
      : p_handler( nullptr )
    {
      init( TupleT() );
    }

  private:

    StaticMessageHandlerInterface* p_handler;

    typedef std::map< int, MessageCallbackInterface* > MessageCallbackMap;
    MessageCallbackMap m_callbackMap;

    template< std::size_t I = 0, typename ... T >
    inline typename std::enable_if< I < sizeof ... (T), void >::type
    init( const std::tuple< T... >& t )
    {
      const auto& msg( std::get<I>( t ) );

      m_callbackMap[ msg.getMessageID() ] =
        new MessageCallbackHandler <
          std::tuple_element< I, std::tuple< T ... > >,
          StaticMessageHandlerInterface
         > ( *p_handler ) ;

      init< I + 1, T... >( t );
    }

    template< std::size_t I = 0, typename ... T >
    inline typename std::enable_if< I == sizeof... (T), void >::type
    init( const std::tuple< T... >& ) { }

};

//----------------------------------------------------------------------
//
int
main( const int, const char** )
{
  typedef std::tuple< MessageOne, MessageTwo, MessageTen > Foople;

  typedef MessageFactory< Foople > FoopleFactory;
  FoopleFactory factory;
}

from which, GCC complains...

test.C: In instantiation of 'void MessageCallbackHandler(MessageT, HandlerT)::receiveMsg(const MessageBase*) [with MessageT = std::tuple_element(2u, std::tuple(MessageOne, MessageTwo, MessageTen) ); HandlerT = StaticMessageHandlerInterface]': test.C:143:5:
required from here test.C:83:11: error: no matching function for call to 'StaticMessageHandlerInterface::processMsg(const std::tuple_element(2u, std::tuple(MessageOne, MessageTwo, MessageTen) )&)' r_handler.processMsg( *msg ); ^ test.C:83:11: note: candidates are: test.C:57:22: note: virtual void StaticMessageHandlerInterface::processMsg(const MessageOne&) virtual void processMsg( const MessageOne& ) { } ^ test.C:57:22: note: no known conversion for argument 1 from 'const std::tuple_element(2u, std::tuple(MessageOne, MessageTwo, MessageTen) )' to 'const MessageOne&' test.C:58:22: note: virtual void StaticMessageHandlerInterface::processMsg(const MessageTwo&) virtual void processMsg( const MessageTwo& ) { } ^ test.C:58:22: note: no known conversion for argument 1 from 'const std::tuple_element(2u, std::tuple(MessageOne, MessageTwo, MessageTen) )' to 'const MessageTwo&' test.C:59:22: note: virtual void StaticMessageHandlerInterface::processMsg(const MessageTen&) virtual void processMsg( const MessageTen& ) { } ^ test.C:60:22: note: no known conversion for argument 1 from 'const std::tuple_element(2u, std::tuple(MessageOne, MessageTwo, MessageTen) )' to 'const MessageTen&'

(Sorry for the Markdown butchering. May be easier just to compile it.)

Upvotes: 1

Views: 899

Answers (1)

Dark Falcon
Dark Falcon

Reputation: 44181

You need to access the inner typedef to get the actual element type. Right now you're passing in the std::tuple_element type.

  m_callbackMap[ msg.getMessageID() ] =
    new MessageCallbackHandler <
      typename std::tuple_element< I, std::tuple< T ... > >::type,
      StaticMessageHandlerInterface
     > ( *p_handler ) ;

Upvotes: 1

Related Questions