Environment Windows XP and 7, Visual Studio 2008, C++. Experience level: novice with TCP/IP, but do have the application working with CAsyncSocket.
The message application receives telemetry data from some hardware (arrives fast and furious), processes it and dispatches the results to a display device via TCP/IP. It sends the data to the client but does not receive from the client.
Setup: the TCP “Manager” is started by the main application and it listens for the client. On connect, it creates the “Sender” that does all the sending.
Question: what is the best, read that simple and maintainable, way for the manager to get the Sender pointer to the main application so it can use the Sender? When the client closes the connection, how is the application informed of that?
Tentative proposal: the application provides the Manager with a pointer to itself. When the connect is made the Manager calls an application method such as Set_TCP_Sender_Pointer( C_Sender_Class *new_sender_pointer );
The Manager can pass the pointer (to the main application) to the Sender on creation. When the Sender gets a close from the client, it calls the same method using a NULL for the argument. This makes the application aware there is no Sender. Then the Sender exits. (There is only one thread so I think there is no concern about the application using a pointer to a non-existent Sender.)
I am pretty sure that will work, but if you had to pick up on this project, would it be easy to understand and maintain? Presume you are not an expert with TCP/IP in the Microsoft world. I am open to alternative suggestions.
After writing the OP I outlined the process with all the steps then began to implement.
I have started using a common directory for re-usable code. In order for the Manager to call a method in its creator it must know the name of the owner's class. To do that I added a forward declaration in the Manager.
However, this means that when the class is re-used, and the owner has a different name, the Manager must be changed.
There is another indicator this is a bad practice. After the forward declaration in the dot H file, the dot CPP file needs to reference the dot H file of the owner. The common code resides in another directory and it cannot find the central code of the main project unless it is specifically spelled out. Then, if the project is moved or re-name, the common code must change.
There may be a way to do this automatically using directive and path names within Visual Studio. But I now think that even if that can be done it would be miss-guided.
Conclusion: A utility class can call "down" the heiarchy to objects it creates, but should not call up to its owner. While the concept of it calling up may make the owner code neater, it represents an inversion of authority. (Regardless of how the methods are name, a call up in an inversion.)
Problems remain: The main application uses pointers into the lower level objects to accomplish its tasks. How does it become aware that the lower level needs to exit, or maybe even has already exited.
Controlled exits: When the TCP sending function detects that the client has closed the connection it does not exit right away. It waits until the upper level code calls the method to send the data. Then the lower level returns an error code stating that the data has not been sent and that the object is exiting.
Uncontrolled exits: I generally do not like exceptions, but this appears to be a time when an exception is warranted. When the lower level must terminate unexpectedly, then an exception could work its way back to the upper level and the exception handler can NULL the pointer.
I personally never used exceptions for ATL projects. Now a colleague votes strongly for throwing exceptions instead of returning HRESULTs. I did some research, and even here on codeproject I don't find much about ATL and exceptions. To me it appears that exceptions in ATL are not very popular. So I would like to know from you ATL guys:
Do you use exceptions? Or you simply return an HRESULT?
I used a combination. A colleague and I developed an exception object with various constructors that could, for example, be thrown with parameters of: a HRESULT, a string describing what failed, the file name it was thrown in, and the line number of the file it was thrown in.
The exception object constructor then took care of writing the error as an event to the System Event log as well as looking up any messages from the HRESULT, or accessing any other error handling mechanism needed.
This resulted in code unclutered with complicated error handling and a standard way of making sure that the log showed not only an error code or message, but a comment on what was being attemted, which .cpp file this was detected in and the line number of the file.
I agree when you say you'd rather return an error code than throw an exception. I found using exceptions locally makes the code clearer and imposes some structure. Having caught the exception locally and dealt with it i.e. usually recorded what exactly went wrong etc I too then prefered to return an error code.
Last Visit: 31-Dec-99 18:00 Last Update: 7-Mar-14 2:56