JHeni
JHeni

Reputation: 751

C++ get template type from object

I'm currently developing a GUI Application. My GUI Application class is a singleton which depend on a templated Mutex and a templated event processing task.

//GUIApplication.hpp

#include <cassert>
template<typename Task_T, typename Mutex_T>
class GUIApplication
{
  public:
    GUIApplication()
    {
      m_pInstance = this;
    }
    static Mutex_T& getMutexInstance()
    {
      assert(nullptr != m_pInstance);
      return m_pInstance->m_GuiMutex;
    }
  private:
    inline static GUIApplication<Task_T, Mutex_T>* m_pInstance = nullptr;

    Task_T m_eventTask;
    
    Mutex_T m_GuiMutex;
};

Now I have some other classes which need a lock the mutex before doing stuff. However I do not want to make templates out of these other classes. Is there a way "derive" or "save" the template type from the current instance of GUIApplication.

//Foo.hpp
class Foo
{
  public:
  Foo();
};

//Foo.cpp
#include <mutex>
Foo::Foo()
{
  //here (obviously) the template auto argument deduction fails
  auto lock = std::scoped_lock(GUIApplication::getMutexInstance());
  //do stuff here....
}

Thx guys for your help :)

Upvotes: 0

Views: 95

Answers (2)

Jan Schultke
Jan Schultke

Reputation: 39588

Obviously you can't avoid ever specifying template arguments for GUIApplication, so GUIApplication::getMutexInstance cannot work.

(1) Building GUIApplication from aliases

However, clever use of aliases can help you avoid repeating yourself:

using Mutex = std::mutex;
using Task = MyTask;
using App = GUIApplication<Task, Mutex>;

auto lock = std::scoped_lock(App::getMutexInstance());

(2) Extracting aliases from GUIApplication

Similarly, you can add alias members to GUIApplication that make it easier to extract template parameters back out of it:

template<typename Task_T, typename Mutex_T>
class GUIApplication {
  public:
    using Task = Task_T;
    using Mutex = Mutex_T;
  // ...
};

// ...
using App = GUIApplication<MyTask, std::mutex>;
using Task = App::Task;
using Mutex = App::Mutex;

You frequently see this patten in the standard library, e.g. std::string::size_type, std::vector::value_type, etc.

Upvotes: 1

Maarten Bamelis
Maarten Bamelis

Reputation: 2423

You can implement a custom type trait to grab a template parameter by index.

I've made a quick and dirty implementation that relies on std::tuple and std::tuple_element_t but you can also properly implement it without those (if so desired).

#include <tuple>
#include <type_traits>

template <int I, typename T>
struct template_param;

template <std::size_t I, template <typename...> typename T, typename... TArgs>
struct template_param<I, T<TArgs...>>
{
    using type = std::remove_pointer_t<std::tuple_element_t<I, std::tuple<TArgs*...>>>;
};

template <int I, typename T>
using template_param_t = template_param<I, T>::type;

Then use it in code as follows:

#include <type_traits>

template <typename TTask, typename TMutex>
struct GuiApplication
{
};

struct Task;
struct Mutex;

int main()
{
    auto app = GuiApplication<Task, Mutex>{};
    static_assert(std::is_same_v<template_param_t<0, decltype(app)>, Task>);
    static_assert(std::is_same_v<template_param_t<1, decltype(app)>, Mutex>);
}

This will not fix your example that fails to compile though, it is merely an alternative approach to get hold of template parameters.

To actually fix your error, I prefer aliasing GuiApplication as suggested by Jan Schultke and let the compiler deduce everything.

Upvotes: 1

Related Questions