CRTP + Traits class : "no type named..." - c++

I try to implement a CRTP with templated class and I have an error with the following example code :
#include <iostream>
template<class T> class Traits
{
public:
typedef typename T::type type; // <- Error
// : "no type named 'type' in 'class MyClass<double, 3u, 3u>'
static const unsigned int m_const = T::m_const;
static const unsigned int n_const = T::n_const;
static const unsigned int size_const = T::m_const*T::n_const;
};
template<class T0> class Crtp
{
public:
typedef typename Traits<T0>::type crtp_type;
static const unsigned int size = Traits<T0>::size_const; // <- This is OK
};
template<typename TYPE, unsigned int M, unsigned int N>
class MyClass : public Crtp< MyClass<TYPE, M, N> >
{
public:
typedef TYPE type;
static const unsigned int m_const = M;
static const unsigned int n_const = N;
};
int main()
{
MyClass<double, 3, 3> x;
std::cout<<x.size<<std::endl;
return 0;
}
I do not understand what causes this problem nor how to fix it.
In fact my goal is that the CRTP class have to know the template arguments of the derived class WITHOUT passing them as template argument of the CRTP class.
Do you have any ideas how to implement this?
EDIT (relating to the first first) : My CRTP class has to be able to handle derived classes with different number of template parameters

The problem is that MyClass is incomplete in its own base class list, where you instantiate Crtp<MyClass>. But instantiating Crtp<MyClass<...>> requires instantiating Traits<MyClass<...>> which then requires instantiating MyClass<...>::type which is impossible because it's incomplete. You have a circular dependency.
In fact my goal is that the CRTP class have to know the template arguments of the derived class
That can be done using a partial specialzation and a template-template-parameter, like so:
#include <iostream>
template<typename T> class Crtp;
template<template<typename, unsigned, unsigned> class T, typename U, unsigned M, unsigned N>
class Crtp< T<U, M, N> >
{
public:
typedef U crtp_type;
static const unsigned int size = M * N;
};
template<typename TYPE, unsigned int M, unsigned int N>
class MyClass : public Crtp< MyClass<TYPE, M, N> >
{
};
int main()
{
MyClass<double, 3, 3> x;
std::cout<<x.size<<std::endl;
return 0;
}

Related

Non verbose solution to make a class member variable optional based on the type of template?

I want the class member variable only_if_int to be defined only if the class is instantiated with the template type int. I have a working solution, but it is too verbose.
#include <iostream>
#include <type_traits>
template<typename T, typename Enable = void>
class MyClass;
template<typename T>
class MyClass<T, std::enable_if_t<std::is_same<T, int>::value>>{
public:
int common;
int only_if_int;
MyClass()
{
common = 0;
only_if_int = 0;
}
void alter_values()
{
common++;
only_if_int++;
}
};
template<typename T>
class MyClass<T, std::enable_if_t<!std::is_same<T, int>::value>>{
public:
int common;
MyClass()
{
common = 0;
}
void alter_values()
{
common++;
}
};
int main()
{
MyClass<int> int_class;
MyClass<float> float_class;
int_class.alter_values();
float_class.alter_values();
std::cout<<"\n int_class "<<int_class.common<<" "<<int_class.only_if_int;
std::cout<<"\n int_class "<<float_class.common<<" ";
return 0;
}
In the above code, the templated class MyClass is defined twice(too much code duplicate). For eg: alter_values function is written twice. Is there a less verbose way, maybe with the help of constexpr std::is_same<T, int> or a different C++ feature to do the same thing?
One approach would be to have a type that contains an int member only if it's instantiated with int, like this:
template<typename>
struct OnlyInt {};
template<>
struct OnlyInt<int> {
int only_if_int;
};
Then MyClass can just inherit from this type:
template<typename T>
class MyClass : public OnlyInt<T> {
public:
int common;
// ... contains only_if_int if T == int
};
Now all uses of only_if_int will need to be wrapped in an if constexpr, and you have to use this-> to indicate that the member is from a base class. So for example:
only_if_int = 0;
becomes:
if constexpr (std::is_same_v<T, int>)
this->only_if_int = 0;
etc.
Here's a demo.

How to pass certain typedef of a class to template

In the following task I want to create a template which only accepts typedefs defined in the following class CDataFormat:
class CDataFormat{
public:
typedef unsigned short element_t;
typedef unsigned int accumulation_t;
typedef double division_t;
};
Now the following implemention works fine.
template<typename DF, int SIZE>
class CFilter{
private:
DF m_memberName[SIZE];
public:
void whatever(){
//CFilter<CDataFormat::division_t, 8> smth; // Just a small test
}
};
However it's not ensured that the template only accepts a member of CDataFormat.
How do I do it?
You might use static_assert to report misusage:
template <typename DF, int SIZE>
class CFilter{
static_assert(std::is_same<CDataFormat::element_t, DF>::value
|| std::is_same<CDataFormat::accumulation_t, DF>::value
|| std::is_same<CDataFormat::division_t, DF>::value, "Unexpected type");
private:
DF m_memberName[SIZE];
};
You can use a helper class and static_assert to meet your needs.
#include <type_traits>
class CDataFormat{
public:
typedef unsigned short element_t;
typedef unsigned int accumulation_t;
typedef double division_t;
};
template <typename T> struct is_valid_type
{
static const bool value = std::is_same<T, CDataFormat::element_t>::value ||
std::is_same<T, CDataFormat::accumulation_t>::value ||
std::is_same<T, CDataFormat::division_t>::value ;
};
template<typename DF, int SIZE>
class CFilter{
static_assert(is_valid_type<DF>::value, "Unsupported type");
private:
DF m_memberName[SIZE];
public:
void whatever(){
}
};
With that,
CFilter<CDataFormat::element_t, 10> f1;
will work without any problem but use of
CFilter<int, 20> f2;
will result in a compile time error.

Select right vector with template

I am following this question in order to select the right container, but I am encountering problems.
I have a selector class that must push back into a vector of pointers, but the right one depends on its dimension (1 for vectors, 2 for matrices):
class selector
{
struct formValues : std::vector<coolvector<double>*>, std::vector<coolmatrix<double>*> { };
formValues maps;
public:
selector() { };
template<unsigned int formdim, typename F>
void operator+=(const form<formdim, F> &f)
{
typedef typename form<formdim, F>::storage_type storage_type;
typedef typename std::vector<storage_type*> pointer_type;
// Push to the right vector
formValues<pointer_type> &m = maps;
m.push_back(f.storage.get());
}
};
The forms class have a dimension and a storage, again depending on the dimension, using shared pointers:
template <bool, class if_true, class if_false>
struct type_switch
{
typedef if_false type;
};
template <class if_true, class if_false>
struct type_switch<true, if_true, if_false>
{
typedef if_true type;
};
template <class T> class coolvector {};
template <class T> class coolmatrix {};
template<unsigned int formdim, typename F>
class form
{
public:
form() = delete;
form(const std::string &s) : type(s)
{
storage = std::make_shared<storage_type>();
}
std::string type;
typedef typename type_switch<formdim == 1, coolvector<double>, coolmatrix<double>>::type storage_type;
std::shared_ptr<storage_type> storage;
};
class oneform : public form<1, oneform>
{
public:
oneform() = delete;
oneform(const std::string &s) : form(s) { };
double operator()(unsigned int i) { return i * 2; };
};
class twoform : public form<2, twoform>
{
public:
twoform() = delete;
twoform(const std::string &s) : form(s) { };
double operator()(unsigned int i, unsigned int j) { return i * 2 + j * 20; };
};
The problem is that in the selector::operator+= I get this error:
main.cpp:77:19: error: expected unqualified-id
formValues<pointer_type> &m = maps;
^
Any hints are appreciated!
formValues is not a template, so you can't write formValues<pointer_type>.
You appear to mean
pointer_type& m = maps;
to get the appropriate base class subobject of maps.

Invalid use of incomplete type (chained templated classes)

I am trying to create a filesystem interface so that my micro controller can interface with an SD card (and I decided to implement all of the File System stuff from the ground up). The problem is I don't know what file system will be on the card....It could be FAT16, FAT32, NFTS, ext3, ect.
So I created a the following abstract classes: FileSystem File and Directory. Now that is all fine and dandy but I am on a micro controller so I want to avoid using the new operator.
This leads to my creation of the UnionBase class (not a very helpful name). Basically this class holds a union of all of the different derived classes and allows you to convert between them:
struct BaseFile_t{
};
struct DerivedA : BaseFile_t{
};
struct DerivedB : BaseFile_t{
};
UnionBase<BaseFile_t,DerivedA,DerivedB> var; //Can now pass references
//of this into File system function
//so that they can modify the right
//Derived type (which can then be
//used as if it is the base type)
Now in order to pass this in I have a struct called FileSystemUnion or FSU for short. This basically just defines all of the necessary BaseUnion types.
The real problem is that it seems that it might end up being a type if recursive typedef (which I know is not allowed). Here is a shortened version of my code:
#include <stdio.h>
#include <iostream>
#include <string>
#include <conio.h>
#include <stdlib.h>
#include <fstream>
#include "prototypeInd/templates/UnionBase.h"
using namespace prototypeInd::templates;
template<class arg,class conv>
struct File{
};
template<class arg,class conv>
struct Directory : public File<arg,conv>{
};
template<class arg,class conv>
struct FS{
typedef Directory<arg,conv> Directory;
typedef File<arg,conv> File;
};
template<class arg,class conv>
struct DFile : public virtual File<arg,conv>{
};
template<class arg,class conv>
struct DDirectory : public virtual Directory<arg,conv>, public virtual DFile<arg,conv>{
void foo(typename conv::Directory::UnionType& d){
}
};
template<class arg,class conv>
struct DFS : public virtual FS<arg,conv>{
typedef DFile<arg,conv> File;
typedef DDirectory<arg,conv> Directory;
};
template<class arg,template<class,class> class fsa,template<class,class> class fsb>
struct FSU{
typedef UnionBase<FS<arg,FSU>,fsa<arg,FSU>,fsb<arg,FSU> > FS;
typedef UnionBase<typename ::FS<arg,FSU>::Directory,typename fsa<arg,FSU>::Directory,typename fsb<arg,FSU>::Directory> Directory;
typedef UnionBase<typename ::FS<arg,FSU>::File,typename fsa<arg,FSU>::File,typename fsb<arg,FSU>::File> File;
};
typedef FSU<int,DFS,DFS> thing;
DDirectory<int,thing> d;
int main(int d,char** thing){
}
The error I get is:
invalid use of incomplete type 'struct DDirectory<int, FSU<int, DFS, DFS> >'
Here is UnionBase.h (its huge but don't worry all of this is working):
#ifndef prototypeInd_templates_UnionBase_h
#define prototypeInd_templates_UnionBase_h
#include <type_traits>
template<class Type, uint64_t time,class First,class... Array>
class IndexOf_{
static const bool isSame = std::is_same<First,Type>::value;
public:
static const uint64_t value = isSame ? time : IndexOf_<Type,time+1,Array...>::value;
};
template<class Type, uint64_t time, class First>
class IndexOf_<Type,time,First>{
public:
//static_assert(std::is_same<First,Type>::value,"Not contained in list");
static const uint64_t value = time;
};
template<class Type,class... Array>
using IndexOf = IndexOf_<Type,0,Array...>;
template<class Target, class First, class... Rest>
class ContainsType{
public:
static const bool value = std::is_same<Target, First>::value ? true : ContainsType<Target,Rest...>::value;
};
template<class Target, class First>
class ContainsType<Target,First>{
public:
static const bool value = std::is_same<Target, First>::value;
};
//Best is the highes so far while rest is the rest of the list
template <class Best,class First, class... Rest>
class GetMaxSize{
//typedef typename GetFirstType<Rest...>::value First;
static const bool FirstBigger = sizeof(First) > sizeof(Best);
public:
typedef typename std::conditional<FirstBigger,typename GetMaxSize<First,Rest...>::value,typename GetMaxSize<Best,Rest...>::value >::type value;
};
template<class Best, class First>
class GetMaxSize<Best,First>{
static const bool FirstBigger = sizeof(First) > sizeof(Best);
public:
typedef typename std::conditional<FirstBigger,First,Best >::type value;
};
template<class From,uint16_t index,class UT,class First,class... Array>
struct cast{
static void apply(From** t,UT* f){
if (index == f->GetActive()){
*t = &((First)(*f));
}
else{
cast<From,index+1,UT,Array...>::apply(t,f);
}
}
};
template<class From,uint16_t index,class UT,class First>
struct cast<From,index,UT,First>{
static void apply(From** t,UT* f){
if (index == f->GetActive()){
*t = &((First)(*f));
}
}
};
template<class... Values>
class UnionType{
typedef typename GetMaxSize<Values...>::value internal_t;
internal_t data;
uint16_t active;
public:
template<class CastFrom, class Dummy = typename std::enable_if<ContainsType<CastFrom,Values...>::value, int>::type >
UnionType(CastFrom&& d) : data(reinterpret_cast<internal_t&>(d)),active(IndexOf<CastFrom,Values...>::value){
}
template<class CastTo, class Condition = typename std::enable_if<ContainsType<CastTo,Values...>::value,int>::type >
operator CastTo const&() const{
return reinterpret_cast<const CastTo&>(data);
}
uint16_t GetActive() const{
return active;
}
//This one actually uses casting of the active data type
template<class CastTo, class Condition = typename std::enable_if<!ContainsType<CastTo,Values...>::value,int>::type >
explicit operator CastTo*() const{
CastTo temp;
CastTo* ret = &temp;
cast<CastTo,0,UnionType,Values...>::apply(&ret,this);
return ret;
}
};
namespace prototypeInd{namespace templates{
template<class Base, class Thing>
struct IsPublicBase{
static const bool value = std::is_base_of<Base,Thing>::value && std::is_convertible<Thing*,Base*>::value;
};
template<class Base, class First, class... Rest>
struct AllInheritFrom{
static const bool value = IsPublicBase<Base,First>::value ? AllInheritFrom<Base,Rest...>::value : false;
};
template<class Base, class First>
struct AllInheritFrom<Base,First>{
static const bool value = IsPublicBase<Base,First>::value;
};
template<template<class> class Function,class First,class... Args>
struct AllFullfill{
static const bool value = Function<First>::value ? AllFullfill<Function,Args...>::value : false;
};
template<template<class> class Function,class First>
struct AllFullfill<Function,First>{
static const bool value = Function<First>::value;
};
template<class Base, class... Rest>
class UnionBase{
static_assert(AllInheritFrom<Base,Rest...>::value, "All of the elements of UnionBase must have Base as a public base");
public:
typedef UnionType<Rest...> UnionType;
private:
UnionType internal;
public:
UnionBase() : internal(typename GetFirstType<Rest...>::value()){};
UnionBase(Base&& value) : internal(value){
}
operator UnionType&(){
return internal;
}
Base* operator ->() const{
//return 0;
return &((Base&)internal);
}
};
//template<class Base, class... Rest>
//using UnionBase = UnionBase_<Base,Rest...>*;
}}
#endif
So the real question is: what should I do to make this work? I am open to restructuring a little bit, but after hours of trying everything I can think of I am almost ready to scrap the whole thing and start again.
The problem is that in certain places of code your classes are really incomplete.
According to [class.mem]/1:
A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier.
Within the class member-specification, the class is regarded as complete within function bodies,
default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including such things in nested classes). Otherwise
it is regarded as incomplete within its own class member-specification.
When applied to your code, this means in particular that the class is incomplete within function parameter lists. Now let's look at the definition of DDirectory::foo():
template<class arg,class conv>
struct DDirectory : public virtual Directory<arg,conv>, public virtual DFile<arg,conv>{
void foo(typename conv::Directory::UnionType& d){
}
};
In the instantiation DDirectory<int,thing> conv is FSU<int,DFS,DFS>, so instantiation of it involves instantiation of UnionBases inside, and eventially to this:
static_assert(AllInheritFrom<Base,Rest...>::value, "All of the elements of UnionBase must have Base as a public base");
where one of classes is DDirectory<int,thing>. Remember, all this happens in the deducing the type of the parameter of foo(), so DDirectory<int,thing> is incomplete, and that's what the compiler is saying.
You could try to move that static_assert for example to the constructor of UnionBase, but it doesn't solve other error which I think is impossible to fix, and the reason is the same:
error: invalid application of 'sizeof' to an incomplete type 'DDirectory<int, FSU<int, DFS, DFS> >'
static const bool FirstBigger = sizeof(First) > sizeof(Best);
^~~~~~~~~~~~~
Here is a minimized example reproducing the problem:
#include <type_traits>
template <typename T1, typename T2>
struct BiggerType {
using type = typename std::conditional<(sizeof(T1) > sizeof(T2)), T1, T2>::type;
};
template<typename T>
struct S {
using B = BiggerType<S, int>;
// This causes the instantiation of BiggerType,
// leading to calculation of sizeof(S) which is incomplete
void foo(const typename B::type& bt) {
}
};
int main() {
S<int> s;
}
Or in very compressed form,
template<typename T>
struct S {
// Same problem here
void foo(typename std::conditional<(sizeof(S) > sizeof(int)), S, int>::type&) {
}
};

Accessing nested template parameters without template template arguments

I have this situation:
template<unsigned int N>
class Base
{
public:
Base(){}
int myint[N];
};
template<unsigned int M>
class BaseWrapper : Base<M>
{
public:
BaseWrapper(){}
};
template<typename T>
class User
{
public:
User(){}
//int myint[T::N]; //How to statically allocate using M or N from above?
};
int main(void)
{
User<BaseWrapper<10> > myuser;
// Do something with User::myint here.
}
I want to be able to use the non-type parameter of the template argument to User to statically allocate data in the User class. I know I can use template template parameters to create BaseWrapper<M> inside User but this is not my preferred approach. Any simple methods to do this?
Thanks!
Add static const unsigned int Size = N; to your class.
Example:
template<unsigned int N>
class Base
{
public:
Base(){}
int myint[N];
static const unsigned int Size = N;
};
Then N is accessible as T::Size in your User class.
Solution 1
Declare a const static member data as:
template<unsigned int M>
class BaseWrapper : Base<M>
{
public:
static const unsigned int size = M; //add this line!
BaseWrapper(){}
};
Then use this as T::size in User class:
template<typename T>
class User
{
public:
User(){}
int myint[T::size]; //you can do this!
};
Solution 2
Or if you cannot add size as member (for whatever reason), then you can use this approach as:
template<typename T> struct get;
template<unsigned int N>
struct get< BaseWrapper<N> > //partial specialization!
{
static const unsigned int size = N;
};
template<typename T>
class User
{
public:
User(){}
int myint[get<T>::size]; //get the size!
};
You can expose the template parameter as a static member variable of the class:
template<unsigned int M>
class BaseWrapper : Base<M>
{
public:
static const int counter = M;
BaseWrapper(){}
};
Then you can use this static member in your User class:
template<typename T>
class User
{
public:
User(){}
int myint[T::counter];
};
The simplest way, which already has been mentioned by the others, is to add a static member to Basewrapper which is initialized to N.
However if for some reason you cannot change User, there's also a way to get at N:
template<typename T> struct get_N;
template<unsigned int N> struct get_N<Basewrapper<N> > { unsigned int const value = N; };
Now in your User template you just can write get_N<T>::value.
One advantage of this is that you can adapt any type after the fact without touching its definition, so if you ever want to instantiate User on anything else than a Basewrapper, say on Newbasewrapper, you just add the line
template<unsigned int N> struct get_N<Newbasewrapper<N> > { unsigned int const value = N; };
or if Newbasewrapper takes some type as template argument and provides the value of N as static const member,
template<typename T> struct get_N<Basewrapper<T> > { unsigned int const value = Basewrapper<T>::N; };