423,099 Members | 2,436 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 423,099 IT Pros & Developers. It's quick & easy.

Make templated member function instantiate object of given class type

P: 84
Say I have two classes A and B. I want A to have a templated function "makeObject()", which makes a new object of the given class template-type and stores it internally.

Is the below correct?
Expand|Select|Wrap|Line Numbers
  1. A a;
  2. a.createObject<B>();
Classes + Functions:
Expand|Select|Wrap|Line Numbers
  1. class A
  2. {
  3.     template<typename Tclass>
  4.     unsigned int createObject();
  5. }
  6.  
  7. template<typename Tclass>
  8. unsigned int A::createObject()
  9. {
  10.     Tclass var = new Tclass();
  11.     return 0;
  12. }
  13.  
  14. class B {};
1 Week Ago #1

✓ answered by Xillez

OK, I got it running by doing the first point in this thread: https://stackoverflow.com/a/10632266

I moved the function implementation into the header.

Thanks btw weaknessforcats :-)

Share this Question
Share on Google+
13 Replies


weaknessforcats
Expert Mod 5K+
P: 9,150
I fixed your code. Compare what I did to the original code.


Don't forget to make your member functions public when calling them from outside the class definition.




Expand|Select|Wrap|Line Numbers
  1. class A
  2. {
  3. public:
  4.     template<class Tclass>
  5.     Tclass* createObject();
  6. };
  7.  
  8. template<class Tclass>
  9. Tclass* A::createObject()
  10. {
  11.     return new Tclass();
  12. }
  13.  
  14. class B {};
  15.  
  16. int main()
  17. {  
  18.     A a;
  19.     B* ptr =  a.createObject<B>();
  20. }
1 Week Ago #2

P: 84
Not exactly, but I appreciate the effort :-)

I think you missed the part with "stores it internally". The thing is that the new object should not be sent out, but stored inside.

A thing I forgot to mention: the return of the createObject function is the id (or game-id) of the new object (so returning 0 inside is fine).

The main difference is that the template class is used as the return type of the function in you're code, but not in my code.
1 Week Ago #3

weaknessforcats
Expert Mod 5K+
P: 9,150
The createObject member function would want to store the created object as a private member variable inside its class. That would mean a member variable of type T. That would mean a template class.

One way around that is to use polymorphism on the created objects so the class only needs to store a base class pointer.

If this doesn't work you can design a bag and keep the bag in the class object and use a bag handler to move created objects in and out.

You might look at Microsoft and the VARIANT. Lots of typecasting required but easy to use.
1 Week Ago #4

P: 84
Sorry for late reply.

I tried implementing one of you're solutions, but still have the same problem.

Error:
CMakeFiles/Framework.dir/source/src/main.cpp.o: In function `main':
main.cpp:(.text+0x33): undefined reference to `void A::createEntity<E>()'

Could it be that when the functions are generated at compile-time, it doesn't know about class B yet (aka hasn't beed defined), then once I try to link to the function, it simply doesn't exists? But this would contradict that in my "A.hpp" file, I include "B.hpp", so how can it not be defined?

Expand|Select|Wrap|Line Numbers
  1.     class A
  2.     {
  3.         B* obj;
  4.     public:
  5.         template<class Tclass>
  6.         void createObject();
  7.     };
  8.  
  9.     template<class Tclass>
  10.     void A::createObject()
  11.     {
  12.         if (std::is_base_of<B, Tclass>::value) // Does given class extend B?
  13.             obj = new Tclass();
  14.     }
  15.  
  16.     class B {};
  17.  
  18.     class E : plublic B {};
  19.  
  20.     int main()
  21.     {  
  22.         A a;
  23.         a.createObject<E>();
  24.     }
2 Days Ago #5

weaknessforcats
Expert Mod 5K+
P: 9,150
Your code compiles after I added a forward reference for class B and corrected a misspelling of public in class E:



Expand|Select|Wrap|Line Numbers
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5.  
  6. class B;  //forward reference
  7.  
  8. class A
  9. {
  10.     B* obj;
  11. public:
  12.     template<class Tclass>
  13.     void createObject();
  14. };
  15.  
  16. template<class Tclass>
  17. void A::createObject()
  18. {
  19.     if (std::is_base_of<B, Tclass>::value) // Does given class extend B?
  20.         obj = new Tclass();
  21. }
  22.  
  23. class B {};
  24.  
  25. class E : public B {};
  26.  
  27. int main()
  28. {
  29.     A a;
  30.     a.createObject<E>();
  31. }

The forward reference tells the compiler that there is a class B so don't get all excited. This is sufficient to declare a pointer to B but anything else will require the compiler see the full class definition beforehand.

In this case, class A stores a B*. If B is the base class of a polymorphic hierarchy, the createObject can create a derived class object and store the pointer to it as a base class pointer.

If class A needs to store any old type, then this code won't work.
2 Days Ago #6

P: 84
What compiler are you using? I use cmake (default setup with Ubuntu 17.10)

See if you can compile my project?
2 Days Ago #7

weaknessforcats
Expert Mod 5K+
P: 9,150
I am using Windows 10 and Visual Studio 2013.

Send me your code and I'll compile it. I prefer one file ending with main().
2 Days Ago #8

P: 84
I've managed to compile it in a single file (in zip), using the g++ compiler. It seems to be cmake's compiler...

The file is simplified and built by exchanging includes, with file content with a little cleanup
2 Days Ago #9

P: 84
In fact the file attached compiles even with cmake, so it's a problem with multiple files then. Any clue on how I can compile it with multiple files?
2 Days Ago #10

weaknessforcats
Expert Mod 5K+
P: 9,150
The code compiles and links for me.


The idea behind multiple files is that each file is compiled separately and then the .obj's are linked by the linker to provide your final executable.


All you need do really is compile each of your source files to get the .obj and then in the linkage section of your make you need to specify which obj's to link.


Compiling with multiple files does not use the #include directive of the preprocessor. The #include combines various source files into a thing called a translation unit and it is this engorged file that is sent to the compiler. This allows you do reuse source code in multiple files without needing multiple copies of the source files.

Which area concerns you? Multiple files merged together into one file or multiple objct files merged together into one executable by the linker?
2 Days Ago #11

P: 84
Not really concerned, but it seems as if multiple linked files is the problem. Not really sure why it should be a problem at all though.

As I build the program the cmake compiler tells me what it finished and how many percent is compiled so far, and the Entity class is built before EntityMgr class (so it's available). The EntityMgr.cpp gets it's Entity type from the EntityMgr.hpp file. The main.cpp file knows about it from EntityMgr.hpp.

Console output:
"Scanning dependencies of target Framework
[ 25%] Building CXX object CMakeFiles/Framework.dir/source/ecs/entity/Entity.cpp.o
[ 50%] Building CXX object CMakeFiles/Framework.dir/source/ecs/system/EntityMgr.cpp.o
[ 75%] Building CXX object CMakeFiles/Framework.dir/source/src/main.cpp.o
[100%] Linking CXX executable bin/Framework
CMakeFiles/Framework.dir/source/src/main.cpp.o: In function `main':
main.cpp:(.text+0x33): undefined reference to `unsigned int EntityMgr::createEntity<Entity>()'
collect2: error: ld returned 1 exit status
CMakeFiles/Framework.dir/build.make:146: recipe for target 'bin/Framework' failed
..."

I also tried forward declaration in main.cpp of both EntityMgr and Entity and of Entity in EntityMgr.cpp.
2 Days Ago #12

P: 84
OK, I got it running by doing the first point in this thread: https://stackoverflow.com/a/10632266

I moved the function implementation into the header.

Thanks btw weaknessforcats :-)
2 Days Ago #13

weaknessforcats
Expert Mod 5K+
P: 9,150
Be careful when you say you moved the function implementation to the header.

You moved a template. A template is used to create a function implementation. It is not the function implantation.

I get excited about this because a header file cannot contain anything that generates machine instructions or allocates memory. All that has to be done in a .cpp file.

So the function implementation goes in a .cpp file and the function prototype goes in the header. The linker will attach the one compiled function to all the places that called it.

You include the header in all the other files.

I hope I haven't confused you. I apologize if I have.
11 Hours Ago #14

Post your reply

Sign in to post your reply or Sign up for a free account.