pass pointer to template member of template class - c++

I'm searching for a way to have a function of my template class which accepts as a parameter a member of the template (it could be either one of its arguments or functions).
model.h
template <class T>
class Model
{
public:
Model(){}
void relate(int T::*, int ){}
};
main.cpp
#include "model.h"
class element
{
public:
element();
char getA(){return this->a;}
int getB(){return this->b;}
char a;
int b;
};
int main(int argc, char *argv[]){
Model<element *> model;
return 0;
}
it gives the creating pointer to member of non-class type ‘element*’ error.
I agree with that since pointer must point to some defined type memory area.
My final goal is to tell my class that I want to relate a specific member of its template with some number which later will be used to order different members in a list which later will be displayed.
This wont be used with types such as Model<int> but only with other classes.

Your instantiate a Model<element*> hence this:
void relate(int T::*, int ){}
is (just hypotheically):
void relate(int element*::*, int) {}
Its a "pointer to member of a pointer type". If you instantiate a Model<element> your code works fine. Alternatively you can use std::remove_pointer<T>.

EDIT:
suddenly I'm figuring out... I used a poiter as a template...
but the game changes now...
int main(int argc, char *argv[]){
Model<element> model;
element e;
model.relate(&element::getA(), 0); // <- call to non static-member error
model.relate(&element::getB(), 0); // <- call to non static-member error
model.relate(&element::a, 0); // <- type mismatch error
model.relate(&element::b, 0); // <- no error
}

Related

Instance of C++ template class as a member of another template class

Let's say I have following templated C++ class
#include <cstdint>
template <uint32_t NO_POINTS>
class A
{
public:
struct Point
{
float x;
float y;
};
A(const Point (&points)[NO_POINTS])
{
for (uint32_t point = 0; point < NO_POINTS; point++) {
table[point] = points[point];
}
}
private:
Point table[NO_POINTS];
};
and I would like to use an instance of this class as a private member of the following class:
#include "A.h"
template <uint32_t NO_LUT_POINTS>
class B
{
public:
B(A<NO_LUT_POINTS>::Point (&table)[NO_LUT_POINTS]) : lut(table){}
private:
A<NO_LUT_POINTS> lut;
};
#include "B.h"
int main(int argc, char** argv) {
B<4> foo({{1326.0, 25.0}, {1601.0, 30.0}, {1922.0, 35.0}, {2293.0, 40.0}});
return 0;
}
I have attempted to compile this code but the compiler reports following error
A<NO_LUT_POINTS>::Point is not a type. I don't understand what the reason for this error is. Can anybody explain to me why the compiler reports this error?
This is a common mistake with types nested in template classes. You need to add typename to tell the compiler that Point is a type.
...
public:
B(typename A<NO_LUT_POINTS>::Point const (&table)[NO_LUT_POINTS]) : lut(table){}
...
Beyond solving your problem, however, please notice that Point doesn't depend on the template parameters of A, so you should not nest it in that class. This would remove the necessity for adding typename.

List not a class or implementation

#ifndef __LISTA_H__
#define __LISTA_H__
template<typename VALUE_TYPE>
class List {
public:
virtual unsigned int size() const = 0;
};
#endif
#include <iostream>
using namespace std;
template<typename T>
unsigned int List :: size() const {
return 0;
}
int main() {
List<int> list;
return 0;
}
I am receiving a compile error stating that the list is not a class defined when its clearly defined above:
prog.cpp:16:14: error: 'List' is not a class, namespace, or
enumeration unsigned int List :: size() const {
^ prog.cpp:5:7: note: 'List' declared here class List {
^ 1 error generated.
List is indeed not a class. It is a class template.
List<T>, for some T, is a class.
Write this:
template<typename T>
unsigned int List<T>::size() const {
// ^^^
return 0;
}
Newer GCC gives better diagnostics here, literally telling you what to do:
main.cpp:16:14: error: 'template<class VALUE_TYPE> class List' used without template arguments
16 | unsigned int List :: size() const {
Once that's fixed, you'll need to create something to derive from List<T>, because that's an abstract class, and you can't instantiate (in main) one of them.
Again, GCC is very helpful here:
main.cpp: In function 'int main()':
main.cpp:21:15: error: cannot declare variable 'list' to be of abstract type 'List<int>'
21 | List<int> list;
| ^~~~
main.cpp:5:7: note: because the following virtual functions are pure within 'List<int>':
5 | class List {
| ^~~~
main.cpp:7:26: note: 'unsigned int List<VALUE_TYPE>::size() const [with VALUE_TYPE = int]'
7 | virtual unsigned int size() const = 0;
virtual unsigned int size() const = 0;
You have created an abstract class.
An abstract class is a class that has to be inherited (Or else simply not be used), used mainly for polymorphic inheritance systems. You cannot create an instance of an abstract class because it beats the whole point of its abstractness.
List<int> list;
Here, you are creating an instance of the declared abstract class.
The class is abstract due the fact that one of its methods, size() const = 0; is abstract (because of the = 0).
Furthermore, as the comments suggest, you should recheck your understanding of how classes and templates work, and my extra suggestion is to check what an abstract class is and make sure you understand when to use such classes.
There's another issue - if you want to declare a function as a template you should add template above the function declaration inside the class declaration too.
template<typename VALUE_TYPE>
class List
{
public:
virtual unsigned int size() const
{
return 0;
}
};
#endif
#include <iostream>
using namespace std;
int main() {
List<int> list;
return 0;
}
the problem is you leave size() as an abstract class for ( future inherited class ).
You have to make another class inherited from List and implement size()
Not to implement at List class

Instantiating doubly inherited object properly?

I am trying to use double inheritance, to declare a more complex object, as the sum of two simpler ones. However when I try to do it as follows:
class common_cfullr
{
public:
double core;
double thr;
double thrres;
common_cfullr(double Core, double Thr, double Thrres){
core=Core;
thr=Thr;
thrres=Thrres;
};
~common_cfullr(){};
common_cfullr() :
core(0.0),
thr(0.0),
thrres(0.0)
{}
};
class common_cfull
{
public:
int nelec;
int ms2;
common_cfull (int Nelec,
int Ms2){
nelec =Nelec;
ms2 =Ms2;
};
~common_cfull(){};
common_cfull() :
nelec(0),
ms2(0){}
};
///Structure inheritance
class common :
public common_cfullr,
public common_cfull
{public:
common(){};
~common(){};
};
int main(int argc, char** argv)
{
common cmn();
cmn.nelec=0;
return 0;
}
I get an error from the compiler. How can I use doubly inheritance properly, for this purposes?
Any sugestion will be very appreciated.
(Based on Neik Kirk's comment.) Changing common cmn(); to common cmn; makes your code compile without an error.
When compiling with clang++, the note emitted by the compiler also tells you this:
t.cc:48:13: warning: empty parentheses interpreted as a function declaration
[-Wvexing-parse]
common cmn();
^~
t.cc:48:13: note: remove parentheses to declare a variable
common cmn();
^~

template not in scope

Got this in "char.h"
#ifndef _CHAR_H_
#define _CHAR_H_
#include <stdio.h>
template <unsigned int TChar>
class Char
{
public:
Char(){ *m_data=0; m_len=-1; }
private:
char m_data[TChar+1];
int m_len;
};
#endif
Now with this simple test :
#include "char.h"
void test(Char<TChar> &oo)
{
//...
}
int main(int argc, char *argv[])
{
Char<80> gg;
return 0;
}
I get with gcc :
TChar was not declared in that scope !?
I don't understand, the declaration is in the .h ??
Thanks...
The full implementation of a template class must be in that template class's header (otherwise you are likely to get a linker error).
The compiler needs to have access to the entire template definition (not just the signature) in order to generate code for each instantiation of the template, so you need to move the definitions of the functions to your header. (Inclusion Model).
You have placed the definition correctly. :-)
However in void test(Char<TChar> &oo) the compiler doesn't know what TChar is. Try adding template <unsigned int TChar> above the definition
template <unsigned int TChar>
void test(Char<TChar> &oo){
// ... stuff
}
You seem to be confused about what template class is. When you write:
template <unsigned int TChar>
class Char
{
/* ... */
};
You're telling the compiler how to generate classes named as Char<somenumber>. Every time you use Char<1>, Char<2>, ... with different parameters somewhere in your code, the compiler will create a new class for you.
The name TChar only represents the value that is to be given as the part of the type name and is valid only inside your class template.
This means that when you write Char<80> the compiler looks if he already has that type - which he doesn't, so he goes and creates a new class named Char<80> based on your template. That's why you can't write a function that takes any Char because Char<80> and Char<81> are different types.
What others are suggesting is that you can turn your function into a function template.
// compiler can't know what value should be used here
// it expects an unsigned int inside <>
void test(Char<TChar> &oo)
{
//...
}
With function templates it works the same as with classes.
template<unsigned int TChar>
void test(Char<TChar> &oo)
{
//...
}
Now when you write
Char<80> gg;
test(gg);
The compiler looks at the type of gg, sees that it can match it with your function template, creates a function with TChar evaluated as 80 and all works out nicely :)
To be precise, the call to your function should look like this:
test<80>(gg);
but you don't have to say this explicitly, because the compiler has enough information from the type of gg to work that all out for you.
If you want to pass Char instantiations to non-templated functions, you can achieve this with polymorphism like this:
class CharBase {
public:
virtual char get(int index) = 0;
virtual ~CharBase() {} // virtual destructor is strongly recommended
};
template<unsigned int CharT>
class Char : public CharBase {
public:
char get(int index) { return m_data[index]; }
/* ... */
};
void test(CharBase &myChar) { /* ... */ }
void test(Char<TChar> &oo)
{
//...
}
TChar is undefined for this function to be compiled. The file char.h doesn't define TChar.

(static initialization/template instantiation) problems with factory pattern

Why does following code raise an exception (in createObjects call to map::at)
alternativly the code (and its output) can be viewed here
intererestingly the code works as expected if the commented lines are uncommented with both microsoft and gcc compiler (see here), this even works with initMap as ordinary static variable instead of static getter.
The only reason for this i can think of is that the order of initialization of the static registerHelper_ object (factory_helper_)and the std::map object (initMap) are wrong, however i cant see how that could happen, because the map object is constructed on first usage and thats in factory_helper_ constructor, so everything should be alright shouldnt it ?
I am even more suprised that those doNothing() lines fix the issue, because that call to doNothing() would happen after the critical section (which currently fails) is passed anyway.
EDIT: debugging showed, that without the call to factory_helper_.doNothing(), the constructor of factory_helper_ is never called.
#include <iostream>
#include <string>
#include <map>
#define FACTORY_CLASS(classtype) \
extern const char classtype##_name_[] = #classtype; \
class classtype : FactoryBase<classtype,classtype##_name_>
namespace detail_
{
class registerHelperBase
{
public:
registerHelperBase(){}
protected:
static std::map<std::string, void * (*)(void)>& getInitMap() {
static std::map<std::string, void * (*)(void)>* initMap = 0;
if(!initMap)
initMap= new std::map<std::string, void * (*)(void)>();
return *initMap;
}
};
template<class TParent, const char* ClassName>
class registerHelper_ : registerHelperBase {
static registerHelper_ help_;
public:
//void doNothing(){}
registerHelper_(){
getInitMap()[std::string(ClassName)]=&TParent::factory_init_;
}
};
template<class TParent, const char* ClassName>
registerHelper_<TParent,ClassName> registerHelper_<TParent,ClassName>::help_;
}
class Factory : detail_::registerHelperBase
{
private:
Factory();
public:
static void* createObject(const std::string& objclassname) {
return getInitMap().at(objclassname)();
}
};
template <class TClass, const char* ClassName>
class FactoryBase {
private:
static detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> factory_helper_;
static void* factory_init_(){ return new TClass();}
public:
friend class detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName>;
FactoryBase(){
//factory_helper_.doNothing();
}
virtual ~FactoryBase(){};
};
template <class TClass, const char* ClassName>
detail_::registerHelper_<FactoryBase<TClass,ClassName>,ClassName> FactoryBase<TClass,ClassName>::factory_helper_;
FACTORY_CLASS(Test) {
public:
Test(){}
};
int main(int argc, char** argv) {
try {
Test* test = (Test*) Factory::createObject("Test");
}
catch(const std::exception& ex) {
std::cerr << "caught std::exception: "<< ex.what() << std::endl;
}
#ifdef _MSC_VER
system("pause");
#endif
return 0;
}
The problem is not related to initialization order, but rather to template instantiation.
Templated code is instantiated on demand, that is, the compiler will not instantiate any templated code that is not used in your program. In particular, in your case the static class member FactoryBase<>::factory_helper_ is not being instantiated and thus it does not exist in the final binary, it does not register itself... (you can check this with 'nm' from the gnu toolchain, that will show the list of symbols present in your executable)
Try changing the FactoryBase constructor to this:
template <class TClass, const char* ClassName>
class FactoryBase {
//...
FactoryBase(){
factory_helper_;
}
//...
};
This will force the compiler into actually instantiating the static member in the binary and you should be set. There is no need to create an empty method and calling it.
EDIT: As an answer to the comment, towards the end of paragraph §14.7.1[temp.inst]/1 in the current standard:
Unless a member of a class template or
a member template has been explicitly
instantiated or explicitly
specialized, the specialization of the
member is implicitly instantiated when
the specialization is referenced in a
context that requires the member
definition to exist; in particular,
the initialization (and any associated
side-effects) of a static data member
does not occur unless the static data
member is itself used in a way that
requires the definition of the static
data member to exist.
#define FACTORY_CLASS(classtype) \
class classtype; \
extern const char classtype##_name_[] = #classtype; \
template detail_::registerHelper_<FactoryBase<classtype,classtype##_name_>,classtype##_name_> FactoryBase<classtype,classtype##_name_>::factory_helper_; \
class classtype : FactoryBase<classtype,classtype##_name_>
explicitly instantiating factory_helper_ fixed the issue.