static function in template struct not using default template argument - c++

I have this code which I wasn't able to compile and I was wondering if there's a way around it. Error is - Argument list for class template "a" is missing.
//not compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a::function(5);
return 0;
}
.
//compiling one
template <typename T = int>
struct a {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T a<T>::x;
int main() {
int b = a<int>::function(5);
return 0;
}
Why can it not use the template argument we passed on default and how can we fix that without entering the template parameter?

The default template parameter int can be used without specifying it, you just need to specify that a is a template:
int b = a<>::function(5);
// ^^

is there a way to do without a<>, just a?
In case your class template a is only intended to provide utility static functions and not act as an object (with state), you could use delegation via a function template, which returns a (dummy) a object, followed by using the fact that an object of a given type, say A, can invoke non-static as well as static member functions.
namespace detail {
template <typename T = int>
struct AImpl {
static T x;
static T function(T number) {
return x = number;
}
};
template <typename T>
T AImpl<T>::x;
} // namespace detail
template<typename T = int>
constexpr detail::AImpl<T> a() { return {}; }
int main() {
const auto b_int = a().function(5);
const auto b_char = a<char>().function('a');
(void)b_int; (void)b_char;
}
If you in fact always wants to use deduction and never actually specify the type of the type template parameter (when other than int), you could exchange class template and its static data member and member function by a single function template that wraps a variable with static storage duration:
#include <type_traits>
template<typename T>
T function(T number) {
static T x;
return x = number;
}
int main() {
const auto b_int = function(5);
const auto b_char = function('a');
static_assert(std::is_same_v<decltype(b_int), const int>, "");
static_assert(std::is_same_v<decltype(b_char), const char>, "");
(void)b_int; (void)b_char;
}
This would be an entirely different (and more implicit) API, however.

Related

How to implement a template provding different values for different tyes

I am using a library which define various constants, like JQX_TYPE_INT, JQX_TYPE_LONG, JQX_TYPE_DOUBLE etc.
My problem is to relate values to c++ types.
For example, in order to have a function which works for various types i have to implement a copy of the same code, differing only in the constant, for every c++-type, e.g.:
myObj *createObject(int iSize, int iDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_INT);
return new myObj(iRealSize);
}
myObj *createObject(int iSize, double dDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_DOUBLE);
return new myObj(iRealSize);
}
// ... same for float char, etc.
That is, apart from having to implement the same code several times, i have to use a dummy variable in order to use the constant related to the c++-type.
Is there some sort of template approach to do something like
template<tyepename T>
struct jqx_type {
static const int type;
// ... template magic
}
template<<typename T>
myObj *createObject(int iSize) {
int iRealSize = calcEffectiveSize(iSize, jqx_type<T>::type);
return new myObj(iRealSize);
}
I tried this
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
But this doesn't even compile.
Is there a way to achieve what i intend to do?
Here:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
You redeclared the same template several times with different default arguments. What you actually want is one template with different specializations:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
PS: type is very common name for member type aliases. I would not use it for an int. Maybe type_selector is a better name.
I would go with a much simpler
myObj *createObject(int iSize, int jqx_type) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject(16, JQX_TYPE_DOUBLE);
or if it needs to be constexpr
template <int jqx_type>
myObj *createObject(int iSize) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject<JQX_TYPE_DOUBLE>(16);
but lets assume you need the actual type it could be something like this:
template <typename T>
struct MyObj {
MyObj(std::size_t size, int jqx_type)
: iSize{calcEffectiveSize(size, jqx_type)} { ... }
MyObj * create(std::size_t size) {
constexpr int jqx_type = []() {
if constexpr (std::is_same_v<T, int>) return JQX_TYPE_INT;
if constexpr (std::is_same_v<T, double>) return JQX_TYPE_DOUBLE;
}();
return new MyObj{iSize, jqx_type};
}
};
or if you do want to go the type traits route then you have to specialize the template struct like this:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
But that's as much code as specializing the createObject function.
PS: you can match and mix ideas, too. E.g. template createObject function with the if constexpr lambda.

Templated Automatic Factory Registration

Imagine I had some objects that were all related to some interface class base. However, all of these objects are templated by some integer DIM (dimension). I have assumed all derivatives of base have a static member int number() and a static string InputName. The idea is to register this static method number(). (Really each class will have whole static interface.)
Registration.hpp
template <int DIM>
class objectRegistry
{
public:
template<typename T>
Register()
{
//something like interface_map[T::InputName] = T::number;
}
private:
static inline std::map<std::string, std::function<int ()>> interface_map;
}
Header1.hpp
template <int DIM>
class base
{
public:
static inline const std::string InputName = "base";
static int number() { return 1; };
base(){};
};
//todo: Some Registration Here
Header2.hpp
template <int DIM>
class derived : public base<DIM>, public AutomaticRegister<DIM, derived<DIM>>
{
public:
static inline std::string InputName = "derived";
static int number() { return 4; };
derived(){};
};
//todo: Some Registration Here
hearder3.hpp
template <int DIM, typename extra_type>
class derived2 : public base<DIM>, public AutomaticRegister<DIM, derived2<DIM,extra_type>>
{
public:
static inline std::string InputName = "derived2 " + std::string(typeid(extra_type).name());
static int number() { return 5; };
extra_type member;
};
//todo: Some Registration Here
Can I create an automatic registration system for derived objects (all derived from base); ideally, one that exists in the same header file as the object definition. I would like, for example, objectRegistry<3> to know that derived<3>, derived2<3,int>, and derived<3,double> exist. I have tried these methods:
Best way to for C++ types to self register in a list?
https://www.bfilipek.com/2018/02/factory-selfregister.html
However, because everything is buried in the template< int DIM>, it is never instantiated.
Is there a way I can force derived to instantiated when objectRegistry is instantiated with a particular template value?
Either of the two approaches you linked to will work.
Your problem is that you are using class templates, not classes.
If you did this
class Something : public AutomaticRegister<Something>
{
// ...
};
then you would get automatic registry, because Something is a class.
You have class templates, which are not anything like a type at all.
The registration happens by instantiating the registration class, which is a base class of your class template.
So, in order to instantiate the registration class, you need the stuff you want registered to be treated as a type. Thus, you need to instantiate some part of the class, either by creating an instance of one of the templates...
derived2<1, double> d2_1_double;
or by explicitly instantiating the entire class template...
template class derived2<1, double>;
or by explicitly instantiating some member of the class template, like the number function...
template int derived2<1, double>::number();
or by creating an actual derived class...
struct d2_1_double : derived2<1, double> { };
or some other way to stamp out a class from a class template.
However, a very minor change to the registration class template (adding a type member alias) gives us a mechanism to register them explicitly in bulk, and does not require inheriting from the registration mechanism.
To demonstrate, I added a bit of extremely simple non-production-quality code. To do so, I added a non-standard function to get a unique name for a type that will work for gcc and clang, but no idea about other compilers. It is not necessary, just makes it easier for me.
#include <functional>
#include <iostream>
#include <string_view>
#include <unordered_map>
template <typename ... Ts> struct TypeList { };
template <typename T>
constexpr auto
fname()
{
return __PRETTY_FUNCTION__;
}
class Registry
{
std::unordered_map<std::string_view, std::function<int()>> map;
public:
void insert(std::string_view key, std::function<int()> value) {
assert(map.find(key) == map.end());
std::cout << "Register \"" << key << "\", " << value() << '\n';
map[key] = std::move(value);
}
int operator()(std::string_view key) const {
return map.at(key)();
}
};
template <int DIM>
Registry & registry()
{
static Registry result;
return result;
}
And here is the stuff to do the auto-registration, a modified version of the answer from one of your links.
template <typename T>
class AutoRegister
{
struct helper {
helper() { registry<T::dim>().insert(fname<T>(), T::number); }
};
/* inline */ static helper h;
template<helper&> struct ref { using type = AutoRegister; };
public:
using type = typename ref<h>::type;
};
// NOTE: A bug in gcc forces this usage rather than just using inline.
template <typename T>
typename AutoRegister<T>::helper AutoRegister<T>::h;
Then, with some class templates similar to yours...
template <int DIM>
struct Bar
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 99; }
};
template <int DIM, typename T>
struct Baz
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 86; }
};
template <int DIM, typename ... Ts>
struct Foo
{
static constexpr int dim = DIM;
static int number() { return dim*100 + 42; }
};
and a helper alias template...
template <typename ... Ts>
using RegisterTypes = TypeList<typename AutoRegister<Ts>::type...>;
We can register the stuff we want. The second one has some duplicates just to show that stuff gets registered once.
using Registered = RegisterTypes<Bar<0>, Bar<1>, Baz<1>, Foo<1>>;
using Registered2 = RegisterTypes<Bar<2>, Bar<1>, Baz<1>, Foo<1>>;
int main()
{
}
Running the program results in the following output...
Register "auto fname() [T = Bar<0>]", 99
Register "auto fname() [T = Bar<1>]", 199
Register "auto fname() [T = Baz<1, int>]", 186
Register "auto fname() [T = Foo<1, int>]", 142
Register "auto fname() [T = Bar<2>]", 299
Register "auto fname() [T = Foo<1, int, double>]", 142```

Splatting a struct

I have a bunch of structs like this with increasing number of members, but consistent member naming:
struct one { int a; };
struct two { int a; int b; };
struct three { int a; int b; int c; };
I also have a templated function which I want to have accept one of these struct's members, splatted:
template <typename T, typename ... ARGS> // T will be one, two, or three
void func(ARGS... args); // This should take 1, 2, or 3, int arguments respectively
I want to be able to call this something like:
two foo;
func<two>(splatter(foo));
Where splatter would somehow split foo so that it would resolve to func<two>(foo.a, foo.b).
I can obviously just expand this inline, without splatter, but the code in which I call func is itself happily templated. I've tried using an initializer_list but I can't figure out how to build one based on template type alone.
Unfortunately my compiler also doesn't support constexpr if to splat a call to func or build an initializer_list. Are there any other options available to me?
As far as I know, what you describe cannot be done using c++. Or if it can, than it is a very complicated solution. The reason is, that you would need to somehow store pointers to class member access functions and then call them properly with your actual object.
However, you get a similar functionality with overloading, which is much easier to implement. For example, you could define a call_func which you overload for your types:
#include <array>
// types
struct one {
int a;
};
struct two {
int a;
int b;
};
struct three {
int a;
int b;
int c;
};
template <class T>
struct more_complex_type {
T a;
T b;
};
// template function
template <typename T, typename... ARGS>
auto func(ARGS... args) {
return std::array<T, sizeof...(args)>{args...};
}
// indirection overload
template <class T>
struct call_func_impl {};
template <>
struct call_func_impl<one> {
auto call(one val) { return func<int>(val.a); }
};
template <>
struct call_func_impl<two> {
auto call(two val) { return func<int>(val.a, val.b); };
};
template <>
struct call_func_impl<three> {
auto call(three val) { return func<int>(val.a, val.b, val.c); };
};
template <class T>
struct call_func_impl<more_complex_type<T>> {
auto call(more_complex_type<T> val) { return func<T>(val.a, val.b); };
};
// syntacting sugar
template <class T>
auto call_func(T val) {
return call_func_impl<T>{}.call(val);
}
// tests
auto test_func() { return func<int>(1, 2, 3, 4, 5); }
auto test_func_of_one() {
auto val = one{};
return call_func(val);
}
auto test_func_of_two() {
auto val = two{};
return call_func(val);
}
auto test_func_of_three() {
auto val = three{};
return call_func(val);
}
auto test_func_of_more_complex_type() {
auto val = more_complex_type<double>{};
return call_func(val);
}
This example uses an overloaded struct template to wrap the function call. This might not be necessary for your case, as you do not have templatized types. You actually could just overload call_func. However, this approach allows you to define the call for more_complex_type which is templatized, as partial function overloading is currently not possible in c++.

How to get the value of a non-type template parameter?

Here is the example:
template <int n>
class A { };
class B {
public:
int foo() {
return a.n; // error
}
private:
A<10> a;
};
I want to get the value of instantiated class A<10>'s non-type template parameter in class B other than template A itself, is there a way to do this? Or should I use some other designs to avoid this problem?
You can't access other classes template parameters just like that. The other class has to expose it, for instance:
template <int n>
class A {
public:
static const int num = n;
};
Then you can access it as a.num (or A<10>::num of course)
If you can't get the type to cooperate (by publishing the parameter value), you can extract it yourself with a traits class:
template<class> struct A_param; // not defined
template<int N> struct A_param<A<N>> {
static constexpr int value = N;
};
// a more general implementation would probably want to handle cv-qualified As etc.
Then use A_param<decltype(a)>::value.
If you have a member A<10> your class B already know about the template parameter. Use that value instead. If the template parameter is indeed not named, let A define a member that reflects the template parameter.
1 -
class B {
public:
int foo() {
return n;
}
private:
const int n = 10;
A<n> a;
};
2 -
template <int n>
class A {
public:
static const int template_param = n;
};

Class template argument dependent on constructor

With a templated number wrapping struct:
template <int I> struct Num { static const int n = I; };
and a few overloaded functions:
template <typename T>
Num<0> id(T x) { return Num<0>(); }
Num<1> id(int x) { return Num<1>(); }
Num<2> id(double x) { return Num<2>(); }
Num<3> id(char x) { return Num<3>(); }
I can initialise the m_i member of a Zod struct using decltype and the type of the return argument of id:
template <typename T>
struct Zod {
Zod(T x) { m_i = identity<decltype(id(x))>::type::n; }
int m_i;
};
However, what I'd really like is for the Zod struct to have a second integer template argument initialised to the value which m_i was set to.
template <typename T, int I = ?>
struct Zod { ... }
This seems possible, as the identity/decltype expression evaluates to a compile time constant; for example, this is fine at global scope:
char c;
static const int g = identity<decltype(id(c))>::type::n;
The problem is that the x argument of the constructor is not available in the scope of Zod's template declaration. Can it be done?
It's perfectly possible- just pass in *((T*)nullptr) to obtain an lvalue of any type T regardless of it's constructability. After all, all you actually do with the constructor argument is pass it to id and then decltype that, which is perfectly doable in the template, since you know that the type of x is T.
template<typename T, int I = identity<decltype(id(*((T*)nullptr)))>::type::n> struct Zod {
...
};