0

I'm designing an application that uses one class to manage a TCP connection and one to manage UI elements. The connection manager receives message strings and does minimal processing on them to determine their type. If they're of a known type, the connection manager will pass the strings along to the GUI manager so it can update the UI elements accordingly.

My challenge is this: if I don't want to include header files across classes, how do I permit access to the other class's public functions?

For example:

//message_types.h
typedef void(*MessageHandlerPointer)(std::string);

enum MessageTypes { info_type, time_type, command_type, reply_type,
                    inside_type, update_type, NUM_TYPES };
/////////////////////////////////////////////////////////////////////////
//ConnectionManager.h
class ConnectionManager
{
  string hostname;
  string port;
  int connection_fd;

  string message_types[NUM_TYPES];
  string partial_message;

  void process_message(string message);
  MessageHandlerPointer message_handlers[NUM_TYPES];

  public:
    ConnectionManager(string hostname, string port);
    ~ConnectionManager();

    int connect();
    void disconnect();
    void listen();
};
/////////////////////////////////////////////////////////////////////////
//ConnectionManager.cpp
ConnectionManager::ConnectionManager(string hostname, string port,
                    void (*message_handlers[NUM_TYPES])(string)):
  hostname(hostname), port(port),
  message_types { "i", "t", "c", "r", "I", "u" }
{
  for(int i = 0; i < NUM_TYPES; i++)
  {
    this->message_handlers[i] = message_handlers[i];
  }
}
/////////////////////////////////////////////////////////////////////////
//GuiManager.h
class GuiManager
{
  void info_handler(string msg);
  void time_handler(string msg);
  void command_handler(string msg);
  void reply_handler(string msg);
  void inside_handler(string msg);
  void update_handler(string msg);

  public:
    GuiManager();
    ~GuiManager();

    MessageHandlerPointer message_handlers[NUM_TYPES];
};
/////////////////////////////////////////////////////////////////////////
//GuiManager.cpp
GuiManager::GuiManager()
{
  message_handlers[info_code]    = &info_handler;
  message_handlers[time_code]    = &time_handler;
  message_handlers[command_code] = &command_handler;
  message_handlers[reply_code]   = &reply_handler;
  message_handlers[inside_code]  = &inside_handler;
  message_handlers[update_code]  = &update_handler;
}
/////////////////////////////////////////////////////////////////////////
//generic main.cpp
int main()
{
    GuiManager gm();
    ConnectionManager cm("host", "45123", gm.message_handlers);
}

But C++ doesn't want me to do that, and I vaguely understand why. Member functions aren't free functions. But I was hoping that I could perhaps make the functions somewhat owner- or class-agnostic?

Either way, my idea isn't going to get me where I want to be, so I'd be glad to hear someone else's impression of what the best solution would be.

Also, I recognize that I might be getting a little ridiculous for the sake of modularity in not letting the classes interface with one another directly. Am I missing the point / sacrificing simplicity for principle?

I'm fairly new to OO, so I'm interested in all of the details of any answer. =)

user207421
  • 305,947
  • 44
  • 307
  • 483
pdm
  • 1,027
  • 1
  • 9
  • 24
  • because of [most vexing parse](http://en.wikipedia.org/wiki/Most_vexing_parse). – Axalo Jan 26 '15 at 22:08
  • 1
    `If I don't want to include header files across objects, how do I permit access to the other object's public functions?` While I can certainly understand wishing that C++ provided a reasonable alternative to headers, the simple fact is that it doesn't. Despite its problems, a header is clearly the right way to do this. – Jerry Coffin Jan 26 '15 at 22:11
  • To elaborate on the above comments (by Kerrek and Axalo): the compiler doesn't know whether `GuiManager gm();` is a variable definition or function declaration. Simply write `GuiManager gm;`, `gm` will then be constructed using the default constructor. – woytaz Jan 26 '15 at 22:12
  • 1
    @woytaz But the compiler does know: It's a function declaration. And in order to call the default ctor or 0-init, say `gm{}`. – Peter - Reinstate Monica Jan 26 '15 at 22:14
  • True, by the standard it's interpreted as a function declaration. If you really want to call the default constructor explicitly you can also write `GuiManager gm = GuiManager();`. – woytaz Jan 26 '15 at 22:18
  • If the GUIManager functions don't need a GUI manager state then you can define them static. In that case the GUIManager serves merely as a namespace, and your array of funciton pointers can hold the addresses. But in general @JerryCoffin is right: Headers are the tools of the trade in C/C++. – Peter - Reinstate Monica Jan 26 '15 at 22:20
  • And if you are interested in loose coupling you may want to examine the PIMPL idiom , cf. Herb Sutter(http://herbsutter.com/gotw/_100/) or many other articles. – Peter - Reinstate Monica Jan 26 '15 at 22:22
  • @Axalo this is *not* a mvp. It is simply a maximum munch. An mvp is a little more complicated. – WhozCraig Jan 26 '15 at 22:29
  • @WhozCraig I think it's vexing anyway – Axalo Jan 26 '15 at 22:33
  • @Axalo lol, fair enough. that, it is. – WhozCraig Jan 26 '15 at 22:33
  • You guys are crazy fast, and everyone taught me something. Major kudos and thanks. =D – pdm Jan 26 '15 at 22:38
  • 1
    @WhozCraig Even if I'm nitpicking -- I think that tokenizing would be identical whether `GuiManager gm();` is interpreted as an object definition or function declaration. The ambiguity is resolved after tokenization, so it can't be max munch ("eat as many characters as long as they can constitute a token"). I'm unsure why the function declaration has precedence; the general "if it can be a declaration then it is one" rule shouldn't apply since both are declarations, just of different identifiers. – Peter - Reinstate Monica Jan 26 '15 at 23:34
  • @PeterSchneider you're absolutely right. It is consumptive, but not mm any more than it is mvp. I wonder myself why such a decision was made. i'm sure its in the annals of C/C++ committees somewhere. – WhozCraig Jan 27 '15 at 00:21

1 Answers1

1

So if I have got this right, you want your ConnectionManager to forward messages to your GUIManager, but without having to include the header of GUIManager, just using forward declarations.

On place you get stuck is that as you notice the type of

void GUIManager::handle_info( std::string ) 

which is different from the type of a pointer to a free function

void handle_info (std::string).

To declare a pointer to the former you have to write

typedef void (GUIManager::*MessageHandlerPointer)(string );

I wrote a simplified example (with Foo and Bar :) ), where an instance of Bar forwards a message to an instance of Foo. Here it is:

#include <iostream>
#include <string>

using namespace std;

class Foo;
typedef void (Foo::*FooMessageHandlerPointer)(string );     // this is the type of a pointer to a member of Foo that gets a string and returns void.

class Bar
{
public:
    Bar ( Foo* foo_, FooMessageHandlerPointer ptr )
    :
    foo (foo_),
    p (ptr)
    {}

public:

    void ForwardMessage ( string s )
    {
        (foo->*p)(s);
    }

private:

    Foo* foo;
    FooMessageHandlerPointer p;
};


class Foo
{
public:

    void ProcessMessage (string s)
    {
        cout << "Foo received: " << s << "\n";
    }
};


int main (void)
{
    Foo foo1;

    Bar bar1 ( &foo1, &Foo::ProcessMessage );

    bar1.ForwardMessage( "Hello world!" );

    return 0;
}

Note that when Bar is defined, it has available only a forward declaration of Foo and the type of the member function pointer. Note also, that Bar needs not only a pointer to a function but also a pointer to the instance too. Of course when you create the instance of Bar in the main, you need to have access to the headers.

I hope i got your question right and this helps you. Otherwise it has been a good exercise since I 've been playing with pointers to member functions these days :)

UPDATE: After your comment, I think you may be looking for something like a delegate, something that encapsulates a function to call, whether it is free or member function. Maybe this thread will be helpful

Community
  • 1
  • 1
opetroch
  • 3,929
  • 2
  • 22
  • 24
  • Hmm. You're right about the type conflict, of course, but I had envisioned a solution where Bars would be able to forward messages to Foos without actually needing to be aware of Foo internals. – pdm Jan 26 '15 at 23:41
  • In my example, Bar is not aware of Foo internals. It only has forward declarations. In main(), Bar is setup with a function pointer, so its only main that knows the interface of Foo. Like your example. – opetroch Jan 26 '15 at 23:52
  • Interesting. Perhaps I'm hung up on the Foo pointer living inside Bar? That seems like the kind of interdependence I'm trying to avoid. – pdm Jan 26 '15 at 23:56
  • Yes, there is a pointer, but that's all. It's like Bar knowing there is a class named Foo, but it doesn't know anything about its interface or internals. To decouple it even more, instead of Foo, Bar could keep a pointer to an interface e.g. IMessageProcessor from which Foo derives. – opetroch Jan 27 '15 at 00:02
  • Fair enough. (Human) syntax error on my part. =) What I was originally attempting was an instance of Bar that only wants a pointer to `ProcessMessage()` and doesn't care (or know) where it comes from. – pdm Jan 27 '15 at 00:22
  • Hi, delegates may be what you want. I have put a link at the bottom of my answer (in case someone looks at it in the future). Hope it helps! – opetroch Jan 27 '15 at 09:36