I have two C++ classes such that:
The first class contains a pointer to the second class and has template function that calls second class's public method through a pointer. The function is defined already in the class declaration, for the reason of being a template.
The second class allows the first class to access its private members through friendship mechanism.
Given that, my question is: how do I organize the sources/headers/forward declarations for this situation?
Whatever I tried, it just doesn't compile to an object file.
One sequence is this:
class Class2;
class Class1
{
Class2 * c2;
public:
template<typename T> T DoSomething(T& X)
{
c2->Func();
return X;
};
void FuncFromClass1();
};
class Class2
{
int data;
public:
Class2() : data(0) {};
void Func();
friend void Class1::FuncFromClass1();
};
void Class2::Func()
{
int i;
}
void Class1::FuncFromClass1()
{
int j;
c2 = new Class2;
c2->data = 1;
}
Barks invalid use of incomplete type ‘class Class2’ because it doesn't recognize c2->Func();.
The other one is:
class Class1;
class Class2
{
int data;
public:
Class2() : data(0) {};
void Func();
friend void Class1::FuncFromClass1();
};
class Class1
{
Class2 * c2;
public:
template<typename T> T DoSomething(T& X)
{
c2->Func();
return X;
};
void FuncFromClass1();
};
void Class2::Func()
{
int i;
}
void Class1::FuncFromClass1()
{
int j;
c2 = new Class2;
c2->data = 1;
}
Doesn't recognize friend void Class1::FuncFromClass1();.
The compilation is tried as g++ -c -std=c++11 -Wall test.cpp.
Note I'd rather not make Class1 as entire friend, rather want to keep only one of its methods as a friend to Class2, if at all possible.
Also, I haven't tried the exact same example in Visual Studio in Windows, but saw an entirely isomorphic situation like the one described (within a bigger project) and no complaints came from VS as far as I recall. Is it unique to g++?
Move the implementation of the member function template where definition of Class2 is known.
class Class2;
class Class1
{
private:
Class2 * c2;
public:
// Delcare, don't define
template<typename T> T DoSomething(T& X);
void FuncFromClass1();
};
class Class2
{
private:
int data;
public:
Class2() : data(0) {};
void Func();
friend void Class1::FuncFromClass1();
};
// Define
template<typename T>
T Class1::DoSomething(T& X)
{
c2->Func();
return X;
};
Note that the proposed solution is simple if both classes are defined in one .h file. If the classes are defined in separate .h files, things get a little bit more complex. You'll have to make sure that the .h file where Class1::DoSomething() is defined is #included in every .cpp file where you want to use Class1::DoSomething().
Related
I have a sprite class, which has a templatised data member. It holds an object, which has a pointer to this specialised sprite template class.
That object requires a forward declaration of my sprite class, but since sprite is a template class, I need to include the full header. Therefore I get a cyclic dependancy which I am unable to figure out
Sprite.h
#include "myclass.h"
template<typename SpriteType, typename = typename std::enable_if_t<std::is_base_of_v<sf::Transformable, SpriteType> && std::is_base_of_v<sf::Drawable, SpriteType>>>
class Sprite {
public:
SpriteType s;
myclass<SpriteType>;
Sprite() {
}
auto foo() {
return s;
}
private:
};
myclass.h
#include "Sprite.h"
//a sprite of type T, is going to create a myclass<Sprite<T>>, a pointer of the Sprite<T> is held in myclass.
template<typename T>
class myclass
{
public:
std::shared_ptr<Sprite<T>> ptr;
myclass() {
}
private:
};
How could I solve this cyclic dependency?
So in summary:
-Sprite is a template class.
-Sprite holds an object to another class. This other class holds a pointer to my this templated sprite class.
-This gives me a cyclic dependency, since both classes are now templates, and need to have their implementations written in their header files.
Simplified decoupling, based on #Taekahns solution.
template<typename T>
class myclass
{
public:
std::shared_ptr<T> ptr;
myclass() {
}
private:
};
template<typename SpriteType, typename = typename std::enable_if_t<std::is_base_of_v<sf::Transformable, SpriteType> && std::is_base_of_v<sf::Drawable, SpriteType>>>
class Sprite {
public:
SpriteType s;
// DO NOT PASS SpriteType here, put the whole Sprite<SpriteType>
myclass<Sprite<SpriteType>> t;
Sprite() {
}
auto foo() {
return s;
}
private:
};
One of the great thing about templates is breaking type dependencies.
You could do something like this. Simplified for readability.
template<typename T>
class myclass
{
public:
std::shared_ptr<T> ptr;
myclass() {
}
private:
};
template<typename SpriteType, typename = std::enable_if_t<std::is_base_of_v<base_class, SpriteType>>>
class Sprite {
public:
SpriteType s;
myclass<Sprite<SpriteType>> t;
Sprite() {
}
auto foo() {
return s;
}
private:
};
That is one of many options.
Another option is to use an interface. i.e. a pure virtual base class that isn't a template.
Example:
I think something like this should do it. Starting to get a hard to follow though.
class base_sprite
{
public:
virtual ~base_sprite(){};
virtual int foo() = 0;
};
template<typename T>
class myclass
{
public:
std::shared_ptr<base_sprite> ptr;
myclass() : ptr(std::make_shared<T>())
{
};
};
template<typename SpriteType>
class Sprite : public base_sprite{
public:
myclass<Sprite<SpriteType>> l;
int foo() override {return 0;};
};
I recently got stuck in a situation like this:
class A
{
public:
typedef struct/class {…} B;
…
C::D *someField;
}
class C
{
public:
typedef struct/class {…} D;
…
A::B *someField;
}
Usually you can declare a class name:
class A;
But you can't forward declare a nested type, the following causes compilation error.
class C::D;
Any ideas?
You can't do it, it's a hole in the C++ language. You'll have to un-nest at least one of the nested classes.
class IDontControl
{
class Nested
{
Nested(int i);
};
};
I needed a forward reference like:
class IDontControl::Nested; // But this doesn't work.
My workaround was:
class IDontControl_Nested; // Forward reference to distinct name.
Later when I could use the full definition:
#include <idontcontrol.h>
// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
// Needed to make a forwarding constructor here
IDontControl_Nested(int i) : Nested(i) { }
};
This technique would probably be more trouble than it's worth if there were complicated constructors or other special member functions that weren't inherited smoothly. I could imagine certain template magic reacting badly.
But in my very simple case, it seems to work.
If you really want to avoid #including the nasty header file in your header file, you could do this:
hpp file:
class MyClass
{
public:
template<typename ThrowAway>
void doesStuff();
};
cpp file
#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"
template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
// ...
}
But then:
you will have to specify the embedded type at call time (especially if your function does not take any parameters of the embedded type)
your function can not be virtual (because it is a template)
So, yeah, tradeoffs...
I would not call this an answer, but nonetheless an interesting find:
If you repeat the declaration of your struct in a namespace called C, everything is fine (in gcc at least).
When the class definition of C is found, it seems to silently overwrite the namspace C.
namespace C {
typedef struct {} D;
}
class A
{
public:
typedef struct/class {...} B;
...
C::D *someField;
}
class C
{
public:
typedef struct/class {...} D;
...
A::B *someField;
}
If you have access to change the source code of classes C and D, then you can take out class D separately, and enter a synonym for it in class C:
class CD {
};
class C {
public:
using D = CD;
};
class CD;
This would be a workaround (at least for the problem described in the question -- not for the actual problem, i.e., when not having control over the definition of C):
class C_base {
public:
class D { }; // definition of C::D
// can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
class B { };
C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
// Danger: Do not redeclare class D here!!
// Depending on your compiler flags, you may not even get a warning
// class D { };
A::B *someField;
};
int main() {
A a;
C::D * test = a.someField; // here it can be called C::D
}
This can be done by forward declare the outer class as a namespace.
Sample: We have to use a nested class others::A::Nested in others_a.h, which is out of our control.
others_a.h
namespace others {
struct A {
struct Nested {
Nested(int i) :i(i) {}
int i{};
void print() const { std::cout << i << std::endl; }
};
};
}
my_class.h
#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif
class MyClass {
public:
MyClass(int i);
~MyClass();
void print() const;
private:
std::unique_ptr<others::A::Nested> _aNested;
};
my_class.cpp
#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"
MyClass::MyClass(int i) :
_aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
_aNested->print();
}
I recently got stuck in a situation like this:
class A
{
public:
typedef struct/class {…} B;
…
C::D *someField;
}
class C
{
public:
typedef struct/class {…} D;
…
A::B *someField;
}
Usually you can declare a class name:
class A;
But you can't forward declare a nested type, the following causes compilation error.
class C::D;
Any ideas?
You can't do it, it's a hole in the C++ language. You'll have to un-nest at least one of the nested classes.
class IDontControl
{
class Nested
{
Nested(int i);
};
};
I needed a forward reference like:
class IDontControl::Nested; // But this doesn't work.
My workaround was:
class IDontControl_Nested; // Forward reference to distinct name.
Later when I could use the full definition:
#include <idontcontrol.h>
// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
// Needed to make a forwarding constructor here
IDontControl_Nested(int i) : Nested(i) { }
};
This technique would probably be more trouble than it's worth if there were complicated constructors or other special member functions that weren't inherited smoothly. I could imagine certain template magic reacting badly.
But in my very simple case, it seems to work.
If you really want to avoid #including the nasty header file in your header file, you could do this:
hpp file:
class MyClass
{
public:
template<typename ThrowAway>
void doesStuff();
};
cpp file
#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"
template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
// ...
}
But then:
you will have to specify the embedded type at call time (especially if your function does not take any parameters of the embedded type)
your function can not be virtual (because it is a template)
So, yeah, tradeoffs...
I would not call this an answer, but nonetheless an interesting find:
If you repeat the declaration of your struct in a namespace called C, everything is fine (in gcc at least).
When the class definition of C is found, it seems to silently overwrite the namspace C.
namespace C {
typedef struct {} D;
}
class A
{
public:
typedef struct/class {...} B;
...
C::D *someField;
}
class C
{
public:
typedef struct/class {...} D;
...
A::B *someField;
}
If you have access to change the source code of classes C and D, then you can take out class D separately, and enter a synonym for it in class C:
class CD {
};
class C {
public:
using D = CD;
};
class CD;
This would be a workaround (at least for the problem described in the question -- not for the actual problem, i.e., when not having control over the definition of C):
class C_base {
public:
class D { }; // definition of C::D
// can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
class B { };
C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
// Danger: Do not redeclare class D here!!
// Depending on your compiler flags, you may not even get a warning
// class D { };
A::B *someField;
};
int main() {
A a;
C::D * test = a.someField; // here it can be called C::D
}
This can be done by forward declare the outer class as a namespace.
Sample: We have to use a nested class others::A::Nested in others_a.h, which is out of our control.
others_a.h
namespace others {
struct A {
struct Nested {
Nested(int i) :i(i) {}
int i{};
void print() const { std::cout << i << std::endl; }
};
};
}
my_class.h
#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif
class MyClass {
public:
MyClass(int i);
~MyClass();
void print() const;
private:
std::unique_ptr<others::A::Nested> _aNested;
};
my_class.cpp
#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"
MyClass::MyClass(int i) :
_aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
_aNested->print();
}
I recently got stuck in a situation like this:
class A
{
public:
typedef struct/class {…} B;
…
C::D *someField;
}
class C
{
public:
typedef struct/class {…} D;
…
A::B *someField;
}
Usually you can declare a class name:
class A;
But you can't forward declare a nested type, the following causes compilation error.
class C::D;
Any ideas?
You can't do it, it's a hole in the C++ language. You'll have to un-nest at least one of the nested classes.
class IDontControl
{
class Nested
{
Nested(int i);
};
};
I needed a forward reference like:
class IDontControl::Nested; // But this doesn't work.
My workaround was:
class IDontControl_Nested; // Forward reference to distinct name.
Later when I could use the full definition:
#include <idontcontrol.h>
// I defined the forward ref like this:
class IDontControl_Nested : public IDontControl::Nested
{
// Needed to make a forwarding constructor here
IDontControl_Nested(int i) : Nested(i) { }
};
This technique would probably be more trouble than it's worth if there were complicated constructors or other special member functions that weren't inherited smoothly. I could imagine certain template magic reacting badly.
But in my very simple case, it seems to work.
If you really want to avoid #including the nasty header file in your header file, you could do this:
hpp file:
class MyClass
{
public:
template<typename ThrowAway>
void doesStuff();
};
cpp file
#include "MyClass.hpp"
#include "Annoying-3rd-party.hpp"
template<> void MyClass::doesStuff<This::Is::An::Embedded::Type>()
{
// ...
}
But then:
you will have to specify the embedded type at call time (especially if your function does not take any parameters of the embedded type)
your function can not be virtual (because it is a template)
So, yeah, tradeoffs...
I would not call this an answer, but nonetheless an interesting find:
If you repeat the declaration of your struct in a namespace called C, everything is fine (in gcc at least).
When the class definition of C is found, it seems to silently overwrite the namspace C.
namespace C {
typedef struct {} D;
}
class A
{
public:
typedef struct/class {...} B;
...
C::D *someField;
}
class C
{
public:
typedef struct/class {...} D;
...
A::B *someField;
}
If you have access to change the source code of classes C and D, then you can take out class D separately, and enter a synonym for it in class C:
class CD {
};
class C {
public:
using D = CD;
};
class CD;
This would be a workaround (at least for the problem described in the question -- not for the actual problem, i.e., when not having control over the definition of C):
class C_base {
public:
class D { }; // definition of C::D
// can also just be forward declared, if it needs members of A or A::B
};
class A {
public:
class B { };
C_base::D *someField; // need to call it C_base::D here
};
class C : public C_base { // inherits C_base::D
public:
// Danger: Do not redeclare class D here!!
// Depending on your compiler flags, you may not even get a warning
// class D { };
A::B *someField;
};
int main() {
A a;
C::D * test = a.someField; // here it can be called C::D
}
This can be done by forward declare the outer class as a namespace.
Sample: We have to use a nested class others::A::Nested in others_a.h, which is out of our control.
others_a.h
namespace others {
struct A {
struct Nested {
Nested(int i) :i(i) {}
int i{};
void print() const { std::cout << i << std::endl; }
};
};
}
my_class.h
#ifndef MY_CLASS_CPP
// A is actually a class
namespace others { namespace A { class Nested; } }
#endif
class MyClass {
public:
MyClass(int i);
~MyClass();
void print() const;
private:
std::unique_ptr<others::A::Nested> _aNested;
};
my_class.cpp
#include "others_a.h"
#define MY_CLASS_CPP // Must before include my_class.h
#include "my_class.h"
MyClass::MyClass(int i) :
_aNested(std::make_unique<others::A::Nested>(i)) {}
MyClass::~MyClass() {}
void MyClass::print() const {
_aNested->print();
}
Please look at the following code:
template<class MT>
class ClassA {
public:
int cnt;
};
template<class MT>
class ClassB : public ClassA<MT> {
public:
void test() {
cnt++ ;
}
};
When compiled, g++ gives an error that "cnt" was not declared in the scope.
If I change cnt to this->cnt, it works. However, I am confused. Can someone please explain why would it not work otherwise?
The reason is that cnt is not a dependent name (doesn't depent on the template paremeter), so the rules of the language state that you have to qualify it with this-> or base::.
I figured I'd expand Mark B's answer (though he is totally right).
the rules of the language state that you have to qualify it with this-> or base
The rules of the language are there because there are many situations where the compiler simply cannot know where to look for that name at compile time: cnt could exist in the parent class, or it could not.
Take the following example:
template<class MT>
class ClassA {
public:
};
template<class MT>
class ClassB : public ClassA<MT> {
public:
void test() {
cnt++ ;
}
};
template<>
class ClassA<int>
{
public:
int cnt;
};
How does the compiler know where to find cnt? The parent's members don't exist at declaration time. You can specialize at any time, so this specialization could be in a different file all together, and the specialization in different compilation units could not agree (wacky). The child class could have any sort of parent structure. So the compiler won't even look in the parent (which doesn't exist until instantiation time) until you tell it to.
This leads to the weird (but perfectly logical) behavior below:
template<class MT>
class ClassA {
public:
};
int cnt = 50;
template<class MT>
class ClassB : public ClassA<MT> {
public:
void test() {
cnt++ ;
}
};
template<>
class ClassA<int>
{
public:
int cnt;
ClassA(){cnt=0;}
};
template <>
class ClassB<int> : public ClassA<int>
{
public:
void test() {
cnt++ ;
}
};
int main () {
ClassB<int> bi;
ClassB<float> bf;
bi.test();
bf.test();
std::cout << cnt << std::endl;
std::cout << bi.cnt << std::endl;
}
Which yields
51
1
(so the cnt in the general template does not match the cnt in the specialization).
Compiling a template does NOT automatically look into the base class for names. Here's the only explanation I could find on short notice: http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html
An easy workaround is to add a
using ClassA<MT>::cnt;
somewhere to the definition of your ClassB.