0

I am trying to pass my class' function void write_log(String verbosity, Variant message); to another class which will be threading event loop (ev.h). String and Variant are from the godot namespace.

I have used typedef to make write_log into type Logger using the definition typedef void (godot::MQTT::*Logger)(godot::String, godot::Variant);. This definition is stored in the callee class and not the calling class.

The function being called is defined as void initialize(Logger logger); which is then called with the code below.

libumqtt::Client client;
client.initialize(&MQTT::write_log);

My calling class is godot::MQTT and the callee class is libumqtt::Client.

My error occurs when I try to use write_log.

void Client::initialize(Logger write_log) {
  ...

  write_log("error", "Initializing MQTT Client!!! TypeDEF!!!");

  ...
}

The error message is as below.

➜  Godot-MQTT-Module git:(master) ✗ scons platform=osx bits=64
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
g++ -o src/MQTT.os -c -g -O2 -arch x86_64 -std=c++17 -fPIC -I. -Igodot-cpp/godot_headers -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Isrc -Ilibraries/libumqtt/src src/MQTT.cpp
g++ -o src/client.os -c -g -O2 -arch x86_64 -std=c++17 -fPIC -I. -Igodot-cpp/godot_headers -Igodot-cpp/include -Igodot-cpp/include/core -Igodot-cpp/include/gen -Isrc -Ilibraries/libumqtt/src src/client.cpp
src/client.cpp:120:14: error: called object type 'libumqtt::Client::Logger' (aka 'void (godot::MQTT::*)(godot::String, godot::Variant)') is not a function or function pointer
    write_log("error", "Initializing MQTT Client!!! TypeDEF!!!");
    ~~~~~~~~~^
1 error generated.
scons: *** [src/client.os] Error 1
scons: building terminated because of errors.

I have been trying to figure out why I cannot get the compiler to recognize the function pointer, but have had no such luck so far. I've received errors such as not being able to use indirection and so on while messing around with my code.

I also used these links to help me with learning how to use typedef and pass a function pointer:

Edit: On uninitialized's mention of my code looking correct, but the amount posted is limited, I am pasting the links to the classes and header files from my repo. These links are marked for this specific commit, so the content won't change even when I update the code. Also, this library is MIT, so you won't have to worry about copyright.

Edit 2: Using this answer, I have checked the type of variable I am trying to use write_log in the callee class and it returns what I expected it to. So, I have no idea why the compiler cannot see that this is a function. I am using C++17 to compile my code and C++14 has the same issue (C++11 breaks on other code before even getting to this, so it doesn't provide useful results).

void (godot::MQTT::*)(godot::String, godot::Variant)

Alexis Evelyn
  • 304
  • 5
  • 17
  • I don't know if this would help, but the original write_log function is marked as private in the header file. – Alexis Evelyn Dec 08 '19 at 07:07
  • I marked the function private as the logger class that gets called from write_log is passed in from the game engine. The logger class is written using GDScript. The original write_log I am trying to pass to my second class works just fine, it just won't work statically. – Alexis Evelyn Dec 08 '19 at 13:57
  • I managed to get it to work. It has something to do with passing a reference of the class itself. I used std::invoke to call the function. I will post what I did later, but for people who want to view it now, look at this [Github link](https://github.com/alexis-evelyn/Godot-MQTT-Module/blob/60be1d93bd21311f403abba6e6ea0ec46c1427a5/src/client.cpp#L125). – Alexis Evelyn Dec 09 '19 at 00:57

2 Answers2

1

From the limited snippet of code you've posted, there doesn't seem to be any error. However, the gcc/g++ compiler is known to throw this error if there is syntax issues like semicolon etc... in the lines preceding this function call.

Shalem
  • 1,446
  • 2
  • 22
  • 47
0

To answer my own question, I will be repeating and explaining the code I wrote.

We have two classes which are stored in the two files, MQTT.cpp and Client.cpp. MQTT.cpp has the namespace "godot" and the class name "MQTT" (so, godot::MQTT::). Client.cpp has the namespace "libumqtt" and the class name "Client" (so, libumqtt::Client::).

MQTT has a function that is defined as void write_log(String verbosity, Variant message); It also has the function initialize();. The arguments and return type do not matter for initialize. To initialize the client, I create an object of Client and then call its own method which is also called initialize.

// MQTT.cpp
String MQTT::initialize() {
    // ...

    // This code will initialize the Client class and pass the function `write_log` to the Client object.
    // `write_log` is marked as private in the header file MQTT.hpp.
    libumqtt::Client client;
    client.initialize(*this, &MQTT::write_log); // Initialize Instance of Client

    // ...
}

In Client.hpp, I marked initialize as public and added the line void initialize(godot::MQTT& mqtt_class, Logger logger);. I also added the typedef typedef void (godot::MQTT::*Logger)(godot::String, godot::Variant); so I can just refer to write_log's data type as Logger.

To call the write_log function, I used the below code.

// Client.cpp
void Client::initialize(godot::MQTT& mqtt_class, Logger write_log) {
    // ...

    // Send Test Log
    // Why Invoke? - https://isocpp.org/wiki/faq/pointers-to-members#macro-for-ptr-to-memfn
    std::invoke(write_log, mqtt_class, "error", "Initializing MQTT Client!!! TypeDEF!!!");

    // ...
}

What I was doing wrong was I was trying to call a pointer to a member function without using invoke. Since I am new to cpp, I wouldn't have known about the fact that I cannot just call a pointer directly if it goes to another function that is not a direct part of my class. Using invoke is easier than trying to use ((object).*(ptrToMember)) which has syntax I do not completely understand yet.

What I did was I created a pointer to my class, godot::MQTT, by grabbing the pointer of the keyword this. I then create a reference to the pointer provided by this through the variable godot::MQTT& mqtt_class. After that, calling the function write_log is as simple as providing the reference to the calling class' object and then the pointer to the function in the calling class, as well as the arguments needed to call the function.

As I am a noob to C++, someone with more experience should edit this sentence out and explain why you need both the reference to the calling object on top of the pointer to the function that needs to be called.

Alexis Evelyn
  • 304
  • 5
  • 17