0

I am kind of new to C++ (and StackOverflow). I am trying to get something to work, but I have some #include problems.

I want to call a callback I made (from here), but I am struggling to do this.

This is my code so far. When I include child.hpp in the someclass.hpp file (because it needs information about Child for Callback<Child>), it has a looped include and the compiler crashes.

I have read about forward declarations (would be class Child; in the someclass.hpp file), and after trying I figured out this works, but I also read different opinions about this.

I have all .hpp files guarded with #ifndef CLASSNAME #define CLASSNAME ... #endif

Do I need to change my entire design, or what is the best option in my case?

base.hpp

#include "someclass.hpp"

class Base
{
    protected:
        unique_ptr<SomeClass> someClass;
};

base.cpp

#include "base.hpp"

Base::Base()
{
    this->someClass = make_unique<SomeClass>();
}

child.hpp

#include "base.hpp"

class Child : public Base
{
    public:
        void callbackFunction(std::string data);
        unique_ptr<Callback<Child>> callback;
};

child.cpp

#include "child.hpp"

void Child::callbackFunction(std::string data)
{
    /*does something*/
}

Child::Child()
{
    this->callback = make_unique<Callback<Child>>(this, &Child::callbackFunction);
    //I can call this->callback->call(data); here without problems
    
    this->someClass->setCallback(this->callback);
  //^^^^^^^^^^^^^^^ == base.someClass

}

someclass.hpp

#include "child.hpp" // < does crash compiler due to loop
//> someclass.hpp uses child.hpp
//> child.hpp uses base.hpp
//> base.hpp uses someclass.hpp
// and thus loop

class SomeClass
{
    public:
        void someFunction(std::string data);
        void setCallback(unique_ptr<Callback<Child>> callback);
        unique_ptr<Callback<Child>> callbackInstance;
};

someclass.cpp

//not 100% sure about the type of this parameter
void setCallback(unique_ptr<Callback<Child>> callback)
{
    this->callbackInstance = callback;
}

void SomeClass::someFunction(std::string data)
{
    //here I want to call this "Child::callbackFunction" which should go like "this->callbackInstance->call(data)" ?
}

also in someclass.hpp

template<class T>
class Callback
{
    public:
        Callback(T* instance, void (T::*function)(std::string))
        {
            this->callbackInstance = instance;
            this->callback = function;
        }
        void call(std::string data)
        {
            (callbackInstance->*callback)(data);
        }
    private:
        T *callbackInstance;
        void (T::*callback)(std::string);
};
Bart
  • 115
  • 5
  • `#include_once`? – Wais Kamal Dec 14 '21 at 14:26
  • I believe you are saying you have a circular include problem. Can you put `template class Callback` in its own header? – drescherjm Dec 14 '21 at 14:26
  • Yeah I can put it in its own header, but the include problem would be still the same since the callback needs information about the child class – Bart Dec 14 '21 at 14:29
  • What does "the compiler crashes" look like? Is there a message? – Drew Dormann Dec 14 '21 at 14:30
  • Related; [https://stackoverflow.com/questions/3353831/resolving-a-circular-dependency-between-template-classes](https://stackoverflow.com/questions/3353831/resolving-a-circular-dependency-between-template-classes) – drescherjm Dec 14 '21 at 14:30
  • the compiler crashs gives says "expected class-name before '{' token on child.hpp", if i remove this ```#include "child.cpp"``` from ```someclass.hpp```, it compiles fine – Bart Dec 14 '21 at 14:34
  • You said you are new to C++, but I presume you are not new to programming. You may want to take a look into Boost [Signals2](https://www.boost.org/doc/libs/1_78_0/doc/html/signals2.html), which is signals/slots for C++. – Eljay Dec 14 '21 at 14:34
  • I am not able to use boost due to a closed environment – Bart Dec 14 '21 at 14:38
  • 3
    ***the compiler crashs gives says "expected class-name before '{' token on child.hpp",*** We would not call that a compiler crash. We would call this an error message. A compiler crash could be a bug in the compiler itself. – drescherjm Dec 14 '21 at 15:03
  • `Callback` does not need to know what `Child` actually looks like, since `Callback` merely contains pointers and nothing else, so forward declarations should work just fine. Also, `someclass.hpp` should be including `child.hpp` not `child.cpp`. `base.hpp` include `someclass.hpp`? You did not show what `base.hpp` looks like – Remy Lebeau Dec 14 '21 at 17:21
  • @Bart Can you also add `Base.hpp` and `Base.cpp` to your question and provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – Jason Dec 15 '21 at 08:06
  • Yeah, **```someclass.hpp``` should be including ```child.hpp``` not ```child.cpp```** was a typo in my question. I changed ```child.cpp``` to ```child.hpp``` in my question. I think describing ```base.hpp``` is not important information since it is only a base class for ```Child``` and for now, there is nothing realy more inside it (except the ```#include "someclass.hpp"```). Anyway, should I use a forward declaration in stead of the ```#include "child.hpp"``` in the ```someclass.hpp```? – Bart Dec 15 '21 at 08:07
  • @Bart If you can add `Base.hpp` and `Base.cpp` then i will be able to see exactly where you're going wrong and provide the exact solution. Currently, since you have not provided us with these files so i have to make a guess for how those files looks like. And i can't post an answer based on some guess. So please provide those files(which are very important) to this question – Jason Dec 15 '21 at 08:11
  • I have provided the ```base.hpp``` and ```base.cpp``` – Bart Dec 15 '21 at 08:26
  • @Bart Now i have added the solution as an answer to *"expected class-name before '{' token on child.hpp"*. Check it out. – Jason Dec 15 '21 at 08:28

1 Answers1

0

To solve the mentioned error("expected class-name before '{' token on child.hpp") you should remove the #include "someclass.hpp" from base.hpp and replace it with a forward declaration for class SomeClass as shown below.

base.hpp

#ifndef BASE_H
#define BASE_H
//NO NEED TO INCLUDE someclass.hpp
#include <memory>
class SomeClass;//FORWARD DECLARE SomeClass
class Base
{
    std::unique_ptr<SomeClass> someClass; 
    public:
    //add declaration for default constructor 
    Base();
};
#endif

base.cpp

#include "base.hpp"
#include "someclass.hpp"

//other things here

Base::Base()
{
    this->someClass = std::make_unique<SomeClass>();
}

child.hpp

#ifndef CHILD_H
#define CHILD_H
#include "base.hpp"
#include <memory>
#include "someclass.hpp"

class Child : public Base
{
    public:
        void callbackFunction(std::string data);
        std::unique_ptr<Callback<Child>> callback;
        //add declaration for default constrcutor 
        Child();
};
#endif

child.cpp

#include "child.hpp"

void Child::callbackFunction(std::string data){
    /*does something*/
}

Child::Child()
{
    this->callback = std::make_unique<Callback<Child>>(this, &Child::callbackFunction);
    //I can call this->callback->call(data); here without problems
}

someclass.hpp

#ifndef SOMECLASS_H
#define SOMECLASS_H
#include <string>
//REMOVED include child.hpp from here
class SomeClass
{
    public:
        void someFunction(std::string data);

        //I think I need an instance of Callback<Child> here?
};
template<class T>
class Callback
{
    public:
        Callback(T* instance, void (T::*function)(std::string))
        {
            this->callbackInstance = instance;
            this->callback = function;
        }
        void call(std::string data)
        {
            (callbackInstance->*callback)(data);
        }
    private:
        T *callbackInstance;
        void (T::*callback)(std::string);
};
#endif

someclass.cpp

#include "someclass.hpp"
void SomeClass::someFunction(std::string data)
{
    //here I want to call this "Child::callbackFunction" which should go like "this->callbackInstance->call(data)" ?
}

The above program compiles and executes successfully as can be seen here.

Summary

Some of the changes that i made are listed below:

  • Removed unnecessary includes
  • Added declarations for default constructor in child.hpp and base.hpp
  • Added include guards in all headers.
Jason
  • 36,170
  • 5
  • 26
  • 60
  • Is it bad practice what I did? aka, including other ```.hpp``` files inside a ```.hpp``` file? – Bart Dec 15 '21 at 08:32
  • Also do I need to include the ```someClass.hpp``` in also the ```child.cpp```? Because if I dont, I get a ```"invalid use of incomplete type 'class SomeClass'``` inside ```child.hpp``` while compiling – Bart Dec 15 '21 at 08:40
  • @Bart I have added other files also in my answer. Check it out. I have written comments wherever i have made the changes. The output of the modified program can be seen [here](https://onlinegdb.com/xzD0vx6cC4). – Jason Dec 15 '21 at 08:53
  • @Bart It is not a bad practice to include other `.hpp` files inside a `.hpp` file. Just include the files that are really necessary and also keep track(meaning take care of) of **cyclic dependency**. – Jason Dec 15 '21 at 09:01
  • Ok, I see you removed the ```child.hpp``` include from ```someclass.hpp```. But if I want to call the callback from the ```Callback callbackInstance``` in ```someclass.cpp```, then it is beter to use a forward decleration or include ```child.hpp``` in the ```someclass.hpp``` file? – Bart Dec 15 '21 at 09:11
  • @Bart If you want to call the callback from `Callback callbackInstance` in **someclass.cpp** then you should add `#include "child.hpp"` inside **someclass.cpp**. Note in `.cpp` files you can safely(without worrying) include `.hpp` files. It is only when you include `.hpp` files in other `.hpp` files that you have to take care of cyclic dependency. Also i would recommend using a separate header file for your `templates`. – Jason Dec 15 '21 at 09:17
  • Yes I am planning to move the template ```Callback<>``` to its own ```.hpp``` file. Do I understand it correctly that if I include a ```.hpp``` in a ```.cpp``` file, that I have to add the forward deflaration on the ```.hpp``` file for function declaration that uses a class form a sepperate class as parameter or return type? – Bart Dec 15 '21 at 09:28
  • @Bart Basically yes. – Jason Dec 15 '21 at 09:45