Reputation: 751
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
Reputation: 39588
Obviously you can't avoid ever specifying template arguments for GUIApplication
, so GUIApplication::getMutexInstance
cannot work.
GUIApplication
from aliasesHowever, 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());
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
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