I'm trying to create a class that uses a variable from another class called Curve3, the problem is that I need to initialize this class using another class called LineCurve3 that inherits from Curve3, I'm trying to use template initialization of the class (TubeGeometry) but the compiler gives an error message: error LNK2019: unresolved external symbol, which I assume is because it tries to initialize the variable and call a function from the base class (Curve3) but because I initialize it with LineCurve3 it can't find them, is there a way to make this happen?
#How to initialize templated Class that depends on base class variable with a Derived class?
84 messages · Page 1 of 1 (latest)
When your question is answered use !solved to mark the question as resolved.
Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.
I strongly recommend that you post a reproducible example
Or at minimum post the exact compile error, linker error tells you what failed to link from where
Also your description sounds like you're willingly options into object slicing, which almost always not what you actually want
main.obj : error LNK2019: unresolved external symbol "public: static class std::shared_ptr<class graphics::TubeGeometry<class graphics::LineCurve3> > __cdecl graphics::TubeGeometry<class graphics::LineCurve3>::create(class std::shared_pt
r<class graphics::LineCurve3> const &,unsigned int,float,unsigned int,bool)" (?create@?$TubeGeometry@VLineCurve3@graphics@@@graphics@@SA?AV?$shared_ptr@V?$TubeGeometry@VLineCurve3@graphics@@@graphics@@@std@@AEBV?$shared_ptr@VLineCurve3@g
raphics@@@4@IMI_N@Z) referenced in function "__cdecl createTube(class graphics::Vector3 const &,class graphics::Color const &,class graphics::Vector3 const &,class graphics::Vector3 const &)" (?createTube@@YA@AEBVVector3@graphics@@AEBVCo
lor@2@00@Z) [C:\Users\vitor\Documents\CODE\C++\gui_beginning_project\imgui_example\build\softvis.vcxproj]
not sure what you mean by "willingly options into object slicing"
Willingly opting
It was a typo
So you choose to use object slicing, and that's weird because object slicing is generally undesirable
And I'm asking if your certain that's what you want
Where is your template defined
Often templates rely on implicit instantiation to work, if you have that error then it suggests that in some way the template definition isn't accessible
I don't know what object slicing is, but will look it up later,
the code is too big to put it here
variable initialization inside Tube geometry:
template <typename CurveType>
class TubeGeometry : public BufferGeometry {
...
Curve3::FrenetFrames frames;
initialization of class:
auto geometry = graphics::TubeGeometry<graphics::LineCurve3>::create(curve, 64, 1, 16, false);
function and variable from Curve3 used on TubeGeometry:
class Curve3 {
public:
int arcLengthDivisions{ 200 };
public:
struct FrenetFrames {
std::vector<Vector3> tangents;
std::vector<Vector3> normals;
std::vector<Vector3> binormals;
};
FrenetFrames computeFrenetFrames(unsigned int segments, bool closed);
I was initializing the FrenetFrames variable before like this:
CurveType::FrenetFrames frames;
and it wasn't working also, this is the point of confusion, if I initialize TubeGeometry using LineCurve3, will TubeGeometry have access to the variable and function declared on Curve3 (they are public)?
Somewhere you use TubeGeometry's static member function called create
Probably on that auto geometry = ... line
I'm asking where you've put the definition of create
In what file
Also is this the first time you're using templates?
declaration:
static std::shared_ptr<TubeGeometry> create(
const std::shared_ptr<CurveType>& path,
unsigned int tubularSegments = 64,
float radius = 1,
unsigned int radialSegments = 16,
bool closed = false);
definition:
template <typename CurveType>
std::shared_ptr<TubeGeometry<CurveType>> TubeGeometry<CurveType>::create(const std::shared_ptr<CurveType>& path, unsigned int tubularSegments, float radius, unsigned int radialSegments, bool closed) {
return create(path, Params(tubularSegments, radius, radialSegments, closed));
}
No, I have some understanding, it's the first time I'm using them in this context of inheritance and class initialization
In what file(s)
on tube geometry files
In the .h or .cpp
declaration on .hpp, definition on .cpp
I'm linking with cmake, I think there is a low chance this is the issue
There is the issue then, with templates always put the definitions in the header file
Prefereable inline in the class
The compiler needs to be able to see the whole definition at compile time
so all the functions have to be on the header?
Yeah, just define them in the class definition itself it's the easiest place to do it
By define them in the class definition I mean like this```cpp
template<class T>
class foo
{
public:
void bar()
{
}
};
I don't think this is the issue
there is another class that uses templates and it was done the same way this class I'm trying to change now and it was compiling fine
Except it is
this class (TubeGeometry) that I'm trying to change now was supposed to work only with Curve3, but I need it to work with LineCurve3, this is why I changed to a template initialization
I'll leave it to grumpy to explain because I need to go
You need to put the defintions in the header file simple as. It might compile because either A you are using the definitions in the same cpp file, B nothing is actually referencing these functions or C you have specialized templates so the definitions are compiled somewhere.
But as a rule always put the full definitions for a templated class in the header file. It will work, I'm pretty sure
I will try it, so any class that uses templates I should put all the function definitions on the header along with the declarations?
Yeah, just define them inplace in the class defintion
It will save you a bunch of typing too
do you know where can I read more about this concept? It was hard to come up with search terms to clarify this
I mean if you need something to back it up then here https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
A template isn't really a class, it's a blueprint in which the compiler can stamp out classes. Templates don't really exist until you instantiate one. So when you do instantiate it you get a whole new version of all the methods too. These methods have different signatures and as a result you can't really have one shared definition because the signatures wouldn't match (unless you specialise the template). As a result you also need to provide a defintion for the compiler to see to go along with the decleration signature. The easiest way to do that is to define it inside the class
;asm ```cpp
template <class T>
T foo(){return T{};}
Compilation successful
No assembly generated.
grumpy_pig_skin | 145ms | c++ | x86-64 gcc 13.2 | godbolt.org
^^ see like this the compile produces no code for it at all
;asm```cpp
template <class T>
T foo(){return T{};}
int bar(){return foo<int>();}
Assembly Output
bar():
push rbp
mov rbp, rsp
call int foo<int>()
pop rbp
ret
int foo<int>():
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
grumpy_pig_skin | 82ms | c++ | x86-64 gcc 13.2 | godbolt.org
See how now there is an instanciation a version of that function is made
It's not that I need something to back it up, I just want to learn more, it was hard to come up with terms/keywords to search for...I've also read somewhere that including definitions on headers leads to code bloat because multiple copies of the same function are included in different translation units and also can lead to increase in compilation time, so I'm trying to clarify from first principles the points of confusion
It can but for templates it is unavoidable
The whole idea in templates is lieterally "I'm gonna get the compiler to do work for me" in which case that takes time
hmm that's sad, this shit breaks my head man kkkkk
I will try to come up with other solutions then, do you recommend avoiding templates whenever possible?
I wouldn't worry about it. It will not slow down you compile times by any human measurable amount unless you're instantiating thousands of objects
I would not recommend avoiding them. Templates are a massive part of C++ and an extremely powerful tool
I think this will be used thousands of times though, I will use the TubeGeometry class to build graphs with thousands of nodes
I wouldn't worry about it
It won't make any significant difference
thank you for taking the time to help! I will try this solution and read more about the topics you shared
@left pelican Has your question been resolved? If so, type !solved :)
I've changed TubeGeometry to have function definition instead of declaration and still getting the same error
Send the error again
And send the code for what you did
I think I understand what the problem is now and is because of what you said: "A template isn't really a class, it's a blueprint in which the compiler can stamp out classes. Templates don't really exist until you instantiate one", the function I'm using to create the class is a static function, if I put the function definitions on the header and change to static inline it also gives the same error from before, but if I remove the static it gives another error indicating I have to change the way I'm initializing the class, so my guess is the error is not because of the Curve3 variable, it's because I'm trying to initialize a templated class using a static function, but not sure
I did what you recommended and put all the function definitions inside of the header inside of the class declaration and removed the .cpp file from the compilation process, then changed some of the definitions that had static before and added inline, so static inline, same error from before...I'm thinking about how to change the class so it doesn't rely on static functions on initialization and will try that next
I initialized the class like this:
std::shared_ptr<graphics::LineCurve3> curve = std::make_shared<graphics::LineCurve3>(from, to);
auto geometry = graphics::TubeGeometry::create(curve, 64, 1, 16, false);
and changed the TubeGeometry to work with a Curve3 type instead of making it template based, and it seems somehow this works...not sure why though
!solved
Thank you and let us know if you have any more questions!
This thread is now set to auto-hide after an hour of inactivity
it really depends on what you call "bloat"
the final program will not have much bloat due to putting stuff in headers, as long as the code is correct
but it can lead to duplication and "bloat" across the TUs
and yes that means the compiler does the same job multiple time, but in many cases it doesn't matter
in the end the TUs will be merged into a final program and the duplication is kicked out
that's if we're talking about making one program, this starts getting more complicated if you start accounting for libraries
but usually you'd first ensure your construct is correct, and in most common use cases for templates that means putting the definition in the header
worry about optimizing the binary content after, when you're more familiar with all the toolchain/stack
you'll still have a lot to learn about just regular language construct and how they work
and also whether it's really impactful on your perf (compile/run time, memory, binary size, etc...)
templates combined with class inheritance broke my brain, specially when thinking about memory and class instances, so far it has been the hardest thing to get used to