Kartik Aiyer
Kartik Aiyer

Reputation: 591

How to typedef a function signature for a method in a template class where the parameter is a generic type

I've written a template class

template<typename T, typename U>
class ANotifier : public ACancelable
{
public:
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, std::function<void(T&)> fn ) = 0;
  virtual void Notify( U msgType, T& value ) = 0;
};

template<typename T, typename U>
class ASingleNotifier : public ANotifier<T, U>
{
...
}

template<typename T, typename U>
class AMultiNotifier : public ANotifier<T, U>
{
...
}

The base class provides a function signature in the lambda. I'm having to repeatedly define the function signature everytime I implement the function or want to make a stack variable of the specific function signature.

I was wondering if there is a way to create a typedef

So something like this

template<typename T, typename U>
class ANotifier : public ACancelable
{
public:
  typedef std::function<void(T&)>  NotificationFn
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, NotificationFn fn ) = 0;
  virtual void Notify( U msgType, T& value ) = 0;
};

template<typename T, typename U>
class ASingleNotifier : public ANotifier<T, U>
{
...
  virtual std::weak_ptr<ACancelableToke> RegisterNotification( U msgType, NotificationFn fn );
}

template<typename T, typename U>
class AMultiNotifier : public ANotifier<T, U>
{
...
  virtual std::weak_ptr<ACancelableToke> RegisterNotification( U msgType, NotificationFn fn );
}

Do I need to declare a typedef in each template subclass. Also when actually creating a stack variable to a function of a specific type how would I declare the fn. eg. NotificationFn<uint32_t> fn;

I used what I have above and I get this error:

/Users/kartik/Projects/pancam/hardware/neolib/inc/ANotifier.h:36:76: error: unknown type name 'NotificationFnOld' virtual std::weak_ptr RegisterNotification( U msgType, NotificationFnOld fn )

where NotificationFnOld is the same as NotificationFn

What I want to do is be able to change the function signature in one place (ie add/rem params) and have it bound to the typedef. I don't mind declaring new typedefs in each subclass as long as I can just have one place to change the signature (ofcourse I will have to change the function implementations but I would like to avoid having to modify intermediate variables and stuff).

EDIT

I modified the typedef to a using and still got a similar error. This is what I used... Instead of typedef std::function<void(T&)> NotificationFn; I used using NotificationFnOld = std::function<void( T& )>;

And this is the error I got.

/Users/kartik/Projects/pancam/hardware/neolib/inc/ANotifier.h:36:76: error: unknown type name 'NotificationFnOld' virtual std::weak_ptr RegisterNotification( U msgType, NotificationFnOld fn )

The error is reported on the Subclass ASingleNotifier in the prototype of overriden function RegisterNotification. It doesn't complain when the alias is used in the prototype declared in ANotifier. How do I cleanly use the alias in my subclasses ?

NOTE: NotificationFnOld and NotificationFn are used interchangeably in this question, I actually use only NotificationFnOld in my code.

EDIT

Incase details are needed, here is the code of what I'm trying. There should be no relation with ACancelable but if more info is needed I will provide.

template<typename T, typename U>
class ANotifier : public ACancelable
{
public:
  using NotificationFn = std::function<void( T&, std::weak_ptr<ACancelableToken> )>;
  using NotificationFnOld = std::function<void( T& )>;
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, std::function<void( T&, std::shared_ptr<ACancelableToken> )> fn ) = 0;
  virtual void Notify( U msgType, T& value ) = 0;
};

template<typename T, typename U>
class ASingleNotifier : public ANotifier<T, U>
{
public:
  virtual ~ASingleNotifier()
  { }

  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, std::function<void( T&, std::shared_ptr<ACancelableToken> )> fn )
  {
    std::weak_ptr<ACancelableToken> retval;
    std::unique_lock <std::mutex> lk( m_mtx );
    std::shared_ptr <ATypedCancelableToken<U>> tmp( std::make_shared<ATypedCancelableToken<U>>( *this, msgType ));
    if( m_notifierMap.find( msgType ) == m_notifierMap.end() ) {
      m_notifierMap[ msgType ] = std::make_pair( fn, tmp );
      retval = tmp;
    }
    return retval;
  }

  virtual void CancelWith( std::shared_ptr<ACancelableToken> spBaseToken ) const
  {
    try {
      auto spToken = std::dynamic_pointer_cast<ATypedCancelableToken<U>>( spBaseToken );
      std::unique_lock<std::mutex> lk( this->m_mtx );
      m_notifierMap.erase( spToken->m_msgType );
    }
    catch ( std::bad_cast exp ) { }
  }

  virtual void Notify( U msgType, T& value )
  {
    m_mtx.lock();
    auto it = m_notifierMap.find( msgType );
    if ( it != m_notifierMap.end() && it->second.first ) {
      auto fn = it->second.first;
      m_mtx.unlock();
      fn( value );
    }else {
      m_mtx.unlock();
    }
  }

protected:
  mutable std::map <U, std::pair<std::function<void( T&, std::shared_ptr<ACancelableToken> )>, std::shared_ptr<ATypedCancelableToken<U>>>> m_notifierMap;
  mutable std::mutex m_mtx;
};

template<typename T, typename U>
class AMultiNotifier : public ANotifier<T,U>
{
protected:
  class AmnCancellableToken : public ATypedCancelableToken<U>
  {
  public:
    AmnCancellableToken( const ACancelable& cancellable,
                         U msgType,
                         typename std::list<std::pair<std::function<void( T&, std::shared_ptr<ACancelableToken> )>,std::shared_ptr<AmnCancellableToken>>>::iterator it ) :
        ATypedCancelableToken<U>{ cancellable, msgType }, m_it{ it } { }
    ~AmnCancellableToken() {}
    const typename std::list<std::pair<std::function<void( T&, std::shared_ptr<ACancelableToken> )>,std::shared_ptr<AmnCancellableToken>>>::iterator m_it;
  };

public:
  ~AMultiNotifier() { }
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, std::function<void( T&, std::shared_ptr<ACancelableToken> )> fn )
  {
    std::weak_ptr<ACancelableToken> retval;
    std::unique_lock <std::mutex> lk( m_mtx );
    std::shared_ptr<AmnCancellableToken> token;

    m_notifierMap[ msgType ].push_back( std::make_pair( fn, token) );
    auto it = m_notifierMap[msgType].end();
    token = std::make_shared<AmnCancellableToken>( *this, msgType, --it  );

    m_notifierMap[ msgType ].back().second = token;
    retval = token;
    return retval;
  }

  virtual void CancelWith( std::shared_ptr<ACancelableToken> spBaseToken ) const
  {
    try {
      auto spToken = std::dynamic_pointer_cast<AmnCancellableToken>( spBaseToken );
      std::unique_lock<std::mutex> lk( this->m_mtx );
      if ( !m_notifierMap[ spToken->m_msgType ].empty()) { //If the list of handler is not empty
        m_notifierMap[ spToken->m_msgType ].erase( spToken->m_it ); //Delete the handler in list
        if ( m_notifierMap[ spToken->m_msgType ].empty()) //If the list is now empty
          m_notifierMap.erase( spToken->m_msgType ); // Delete the msgType Key element
      }
    } catch ( std::bad_cast exp ) { }
  }

  virtual void Notify( U msgType, T& value )
  {
    m_mtx.lock();
    auto anotherCopy = m_notifierMap;
    m_mtx.unlock();
    typename std::map<U, std::list<std::pair<std::function<void( T&, std::shared_ptr<ACancelableToken> )>, std::shared_ptr<AmnCancellableToken>>>>::iterator ait =
        anotherCopy.find( msgType );

    if( ait != anotherCopy.end() &&
        !ait->second.empty() ) {
      for( auto ait2 = ait->second.begin(); ait2 != ait->second.end(); ait2++ )
        if( ait2->first )
          ait2->first( value );
    }
  }

protected:
  mutable std::map <U, std::list<std::pair<std::function<void( T&, std::shared_ptr<ACancelableToken> )>,std::shared_ptr<AmnCancellableToken>>>> m_notifierMap;
  mutable std::mutex m_mtx;
};

Thanks for the help / suggestions

Kartik

Upvotes: 0

Views: 559

Answers (1)

kfsone
kfsone

Reputation: 24249

You need to qualify NotificationFn:

  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, typename ANotifier<T,U>::NotificationFn fn );

Or if you are going to be using it multiple times, add a using statement:

using NotificationFn = typename ANotifier<T,U>::NotificationFn;

inside each derived class.

here's a complete example:

#include <iostream>
#include <functional>
#include <memory>

struct ACancelable {};
struct ACancelableToken {};

template<typename T, typename U>
class ANotifier : public ACancelable
{
public:
  using NotificationFn = std::function<void(T&)>;
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, NotificationFn fn ) = 0;
  virtual void Notify( U msgType, T& value ) = 0;
};

template<typename T, typename U>
class ASingleNotifier : public ANotifier<T, U>
{
  virtual std::weak_ptr<ACancelableToken> RegisterNotification( U msgType, typename ANotifier<T,U>::NotificationFn fn );
};

int main() {
    // your code goes here
    return 0;
}

http://ideone.com/sXVkvw

Upvotes: 1

Related Questions