Author Topic: Difficulty using a function object (delegate) when wrapped in a class C++  (Read 2685 times)

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
I'm having difficulty with one final part of my code. I am trying to use a delegate or function object to define how input arguments (switches) are processed. My code works 4.0 when not encapsulated in a class. Once I try putting the code into a class, I receive the following error:

Error   C2440   '<function-style-cast>': cannot convert from 'initializer list' to 'ConsoleSwitch'.

Something is going wrong with how I am setting the values of an array of ConsoleSwitch. When constructing ConsoleSwitch, one of constructor parameters is the function to define how that switch parses its argument, which is what is causing the compile time error. Below is what I think are the relevant snippets of code.

ConsoleArgumentParser class takes console input and parses it into a struct called ConsoleArgs for the rest of the application to use.
Code: [Select]
class ConsoleArgumentParser
{
public:
    ConsoleArgumentParser();
    ConsoleArgs sortArguments(int argc, char **argv);

private:
    ... Rest of class declaration goes here ...
};

ConsoleSwitch is a struct that represents an argument (switch) that can be parsed out of the command line. Its constructor takes a function (delegate) that tells the switch how it is supposed to parse the supplied argument.
Code: [Select]
#include <functional>

struct ConsoleSwitch
{
    ConsoleSwitch() { }

    ConsoleSwitch(string shortSwitch, string longSwitch, function<void(const char* value, ConsoleArgs&)> parseFunction, int numberOfArgs)
    {
this->shortSwitch = shortSwitch;
this->longSwitch = longSwitch;
this->parseFunction = parseFunction;
this->numberOfArgs = numberOfArgs;
    }

    ... Rest of structure here. ...
};

Below is the constructor for ConsoleArgumentParser, where the possible switches are defined by setting each index of the ConsoleSwitches array. This is where the error is thrown on each attempt to construct the consoleSwitch in regards to the function used to parse.

Code: [Select]
ConsoleArgumentParser::ConsoleArgumentParser() 
{
    consoleSwitches[0] = ConsoleSwitch("-H", "--HELP", &ConsoleArgumentParser::parseHelp, 0);
    ... add other possible switches here ...
}



If I remove the code within ConsoleArgumentParser from a class, everything works fine. See below for working initialization code. I've tried several things, but really don't know what is wrong here.

Code: [Select]
ConsoleSwitch consoleSwitches[]
{
    ConsoleSwitch("-H", "--HELP", parseHelp, 0),
    ConsoleSwitch("-?", "--?", parseHelp, 0),
    ConsoleSwitch("-D", "--DESTINATIONDIRECTORY", parseDestDirectory, 1),
    ConsoleSwitch("-Q", "--QUIET", parseQuiet, 0),
    ConsoleSwitch("-O", "--OVERWRITE", parseOverwrite, 0),
    ConsoleSwitch("-C", "--COMPRESSION", parseCompressionFormat, 1),
    ConsoleSwitch("-S", "--SOURCEDIRECTORY", parseSourceDirectory, 1)
};

Any help is appreciated. If we cannot figure it out, I will just leave the ConsoleArgumentParse code wrapped in a namespace and not make it a class and everything will work. Just looking to make it 100%.

-Brett

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Re: Difficulty using a function object (delegate) when wrapped in a class C++
« Reply #1 on: September 26, 2017, 06:20:08 AM »
In order for that to work you also need a this pointer... otherwise the compiler has no idea what object you're trying to use -- I think it's been addressed before but all class member functions have an implicit first parameter of pointer to class... e.g.:

Code: [Select]
class foo
{
public:
    void bar() {}
};

foo myFoo;

myFoo.bar();

myFoo.bar() to the compiler looks something like myFoo.bar(&myFoo). It's probably a bit more complicated than I'm explaining but suffice it to say that there's a lot of syntactic sugar in place to make it less ugly.

Anyway, point being that you need to provide a pointer to the class that you're calling the function on. You can do this using std::bind or by using an existing delegate type class. I don't completely understand std::bind yet but I've used a  delegate class that works pretty well. Can see it here: https://github.com/pbhogan/Signals

I've used this combination extensively for a signals/slots approach and it's very effective. I suspect this is what you're looking to do as well. If nothing else you can use the delegate code.

Offline Hooman

  • Administrator
  • Hero Member
  • *****
  • Posts: 4955
Re: Difficulty using a function object (delegate) when wrapped in a class C++
« Reply #2 on: September 29, 2017, 12:46:42 AM »
Yes, leeor_net is right, it's a problem with the "this" parameter. Essentially there is a calling convention mismatch between the function you are passing, and the function that is expected. You are passing ConsoleArgumentParser::parseHelp, which being a member function takes a hidden this pointer (__thiscall). The ConsoleSwitch constructor expects a regular plain function (__stdcall), which doesn't take a hidden this pointer. If appropriate, you can declare your ConsoleArgumentParser::parseHelp function as static. A static function does not take a this pointer.


See the Microsoft article:
Argument Passing and Naming Conventions

Offline Vagabond

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 1015
Re: Difficulty using a function object (delegate) when wrapped in a class C++
« Reply #3 on: September 29, 2017, 02:19:31 AM »
Leeor_net and Hooman,

Thanks! Setting the private member functions of the class to static fixed it.
Code: [Select]
which being a member function takes a hidden this pointer (__thiscall)

C++ continues to be really complicated!

I was getting ready to just leave the code in the namespace without a class and saw this post, so that is cool!

-Brett

Offline leeor_net

  • Administrator
  • Hero Member
  • *****
  • Posts: 2352
  • OPHD Lead Developer
    • LairWorks Entertainment
Static functions do solve that problem, basically just wraps the function into a class's namespace.

And yeah, C++ is a difficult language. It's often referred as a federation of languages for the fact that it's four languages in one -- C, C++, Template Metaprogramming and STL. And because it's _so_ widely used with _so_ many applications it has a lot of features that take years to learn. And quirks. Many many quirks.