LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   C++: Vector Templates (https://www.linuxquestions.org/questions/programming-9/c-vector-templates-867189/)

Aquarius_Girl 03-08-2011 06:32 AM

C++: Vector Templates
 
Tried this in a normal class,
Code:

template <class derivedComponent, typename returnType, typename messageCode>
std :: vector <SingularCallBack <derivedComponent, returnType, messageCode> > callbackList;

SingularCallBack is the template class.

Compilation error:
Code:

error: data member ‘callbackList’ cannot be a member template
This link: http://www.cplusplus.com/reference/stl/vector/
says:
Quote:

In their implementation in the C++ Standard Template Library vectors take two template parameters:

template < class T, class Allocator = allocator<T> > class vector;
Does that mean I cannot pass 3 arguments?

dwhitney67 03-08-2011 06:40 AM

As the documentation indicates, an STL vector can only accept one or two template parameters. Your example shows it accepting only one parameter.

Yes, the SingularCallback class itself accepts 3 template parameters, but this is not relevant when you declare the vector. Based on the code you posted yesterday, the following is acceptable to the compiler:

Code:

#include <vector>

template <typename Component, typename ReturnType, typename ParamType>
class SingularCallback
{
};

class MyClass
{
};

int main()
{
  typedef SingularCallback<MyClass, bool, const std::string&> SC;

  std::vector<SC> callbacks;
}


johnsfine 03-08-2011 07:05 AM

Quote:

Originally Posted by Anisha Kaul (Post 4282646)
Does that mean I cannot pass 3 arguments?

Hopefully you understood dwhitney67's answer to that part of your question.

The error message was very clear: A data member of a class cannot be a template.

The fact that the data member is a vector doesn't matter. The number of template arguments is not relevant to the problem.

What are you trying to accomplish? What is the purpose of callbackList? What will it contain? How will it be used? Why do you need the specific types of the callbacks it contains to remain unknown when the class containing the vector is instantiated?

I expect you are trying to make a container of objects whose types are not known until run time. That is quite easy in some other languages (Java, Python, etc.). In C++ it is not so easy.

Templating is used for situations in which the types are not known by the source code that defines actions on those types, but is known at compile time.

Classes with virtual methods are generally used for situations in which the final type is not known until run time. But containers of polymorphic objects are generally not supported. Usually we use container of pointers to objects instead. But that has its own set of extra complications (which code is responsible for deleting those objects).

Aquarius_Girl 03-08-2011 10:38 AM

Thanks to both of you,

The situation:
1. The clients would fill up that vector with their desired function names and the message codes.
2. Because the clients would be started after the server, it has to happen on run time.

The class name is supposed to be decided at run time, the functions which the clients would be using for callbacks are NOT derived from base class (i.e. they are not virtual), that's why the example shown in my previous thread won't work in this case.

Please guide.

johnsfine 03-08-2011 02:43 PM

I don't know how much to read into your use of the terms "client" and "server". So for the moment I'm assuming the most abstract (most lacking in information content) interpretation.

Quote:

Originally Posted by Anisha Kaul (Post 4282921)
1. The clients would fill up that vector with their desired function names and the message codes.
2. Because the clients would be started after the server, it has to happen on run time.

None of that actually tells me anything about what can or can't be known at compile time.

Whatever you mean by "client", the client gets compiled at some point and the nature of the client is known at that time.

In the typical use of templates, the "service" is instantiated differently for each "client".

That probably doesn't fit your needs, because I think you want a single instance of the "server" handling polymorphic "clients". Templating is for single source code of the service, but separate instances.

But I also think you haven't thought through what you want. See below.

Quote:

The class name is supposed to be decided at run time, the functions which the clients would be using for callbacks are NOT derived from base class (i.e. they are not virtual)
Languages that support that extreme of polymorphism do so by the equivalent of having a common base class for all objects and something similar to making all member functions virtual. Typically they do that "under the hood" where the programmer doesn't notice. If you want that behavior in C++ you usually need to do the same, only explicitly (really have a common base object for every type that participates in the polymorphism).

You are making four things variable:
1) The class of the object.
2) The selected member function.
3) The parameter type.
4) The return type.

Items 1 and 2 just represent some implementation details once you have a clearer idea what you want to accomplish.

But items 3 and 4 represent a more fundamental problem. At some point in the "server" code, there is a call to the actual function. What could that pass as a parameter? Into what kind of variable could it store the result? What would it even mean for those decisions to be made at run time?

In a more fundamentally polymorphic design, the answer to items 3 and 4 is easy. The parameter is a reference to the base type shared by everything. The return value is also a reference to the base type shared by everything.

In some C++ templated code, the answer to items 3 and 4 is that they are template parameters, which fundamentally implies instantiating that call separately as part of the compilation of every client. It is possible (getting into some pretty tricky templating) that the actual call might be somewhere it could be instantiated separately per client, even if the vector of callbacks exists as a single shared object.

I don't think those implementation details can be abstracted away from the details of the "client/server" relationship. So I can't give you any example of how to do the above even if I had time.

You should look at std::mem_fun as a solution to part of what you're trying to accomplish:

http://www.cplusplus.com/reference/s...ional/mem_fun/

That takes care of allowing items 1 and 2 that I described before getting bound together in a way that lets the combination be used without compile time knowledge of 1 and 2.

Note that the source code invoking mem_fun needs compile time (but not source time) knowledge of all the types, but that code would typically be compiled into your "client".

The server using the result of mem_fun still needs compile time knowledge of the parameter and return types, but doesn't need any compile time knowledge of the type of object to which the member function is bound.

Aquarius_Girl 03-09-2011 03:31 AM

Thank you John, for bothering so much.
Quote:

Originally Posted by johnsfine (Post 4283201)
I don't know how much to read into your use of the terms "client" and "server". So for the moment I'm assuming the most abstract (most lacking in information content) interpretation.

I meant the socket programming, servers and client programs. The base class is common to
both the server and the client programs.

Quote:

Originally Posted by johnsfine (Post 4283201)
Languages that support that extreme of polymorphism do so by the equivalent of having a common base class for all objects and something similar to making all member functions virtual. Typically they do that "under the hood" where the programmer doesn't notice. If you want that behavior in C++ you usually need to do the same, only explicitly (really have a common base object for every type that participates in the polymorphism).

I thought of that before creating this thread, let me explain more:

1. The callback class is declared in the base class inherited both by server and the clients.
2. The clients send messages to server after getting registered and the server is supposed
to forward them based on some conditions, to other clients.
3. In order for server to forward the messages, it needs to know which clients want it,
therefore the clients need to put the desired functions (which intend to work as callbacks) and the corresponding message codes in the vector we are talking of.
4. The clients may have several functions for handling several messages, and putting all
those functions in the base class (virtual) does not make sense to me because they won't be used in base class neither base class should care about them.

Quote:

Originally Posted by johnsfine (Post 4283201)
You are making four things variable:
1) The class of the object.
2) The selected member function.
3) The parameter type.
4) The return type.

The class and the function are definitely variable. But the return type and the parameter
type of all the functions is same and is known beforehand.

Quote:

Originally Posted by johnsfine (Post 4283201)
You should look at std::mem_fun as a solution to part of what you're trying to accomplish:http://www.cplusplus.com/reference/s...ional/mem_fun/

I am finding that function difficult to understand (w.r.t classes) but I'll try again. Thanks.

Quote:

Originally Posted by johnsfine (Post 4282672)
The error message was very clear: A data member of a class cannot be a template.

I tried this: Scroll please.
Code:

#include <iostream>
#include <vector>
using namespace std;

template <class MyDummyClass, typename ReturnType, typename Parameter>
class SingularCallBack
{
public:
          typedef ReturnType (MyDummyClass ::*Method)(Parameter);
         
          SingularCallBack(MyDummyClass* _class_instance, Method _method)
          {
              class_instance = _class_instance;
              method            = _method;
          };

          ReturnType execute(Parameter parameter)
          {
              return (class_instance->*method)(parameter);
          };

private:
          MyDummyClass*  class_instance;
          Method  method;
};


class BaseClass
{
  public:
    virtual bool DerivedMethod (const std::string& str)
    {
          return true;
    }
};

class AClass : public BaseClass
{
public:
  template < class derivedComponent, class Allocator = allocator<derivedComponent> > class callbackList;
             
  AClass ()  {}
 
  bool AMethod( std::string& str)
  {
    std::cout << "AClass[]: " << str << std::endl;
    return true;
  }
};

int main()
{   
  AClass a (); 
  a.callbackList.push_back (SingularCallBack < AClass, bool, const std::string& > (&a, &AClass :: AMethod, "sadsa"));

  return 0;
}

This code compiles if I remove the red statement from main (), the error which I am getting right now is due to lack of understanding (despite your efforts)
Code:

anisha@linux-uitj:~> g++ callback3.cpp -Wall -Wextra
callback3.cpp:31: warning: unused parameter ‘str’
callback3.cpp: In function ‘int main()’:
callback3.cpp:54: error: request for member ‘callbackList’ in ‘a’, which is of non-class type ‘AClass()’
callback3.cpp:54: error: no matching function for call to ‘SingularCallBack<AClass, bool, const std::string&>::SingularCallBack(AClass (*)(), bool (AClass::*)(std::string&), const char [6])’
callback3.cpp:11: note: candidates are: SingularCallBack<MyDummyClass, ReturnType, Parameter>::SingularCallBack(MyDummyClass*, ReturnType (MyDummyClass::*)(Parameter)) [with MyDummyClass = AClass, ReturnType = bool, Parameter = const std::string&]
callback3.cpp:7: note:                SingularCallBack<AClass, bool, const std::string&>::SingularCallBack(const SingularCallBack<AClass, bool, const std::string&>&)

Please help.

dwhitney67 03-09-2011 05:44 AM

Quote:

Originally Posted by Anisha Kaul (Post 4283862)
3. In order for server to forward the messages, it needs to know which clients want it...

You may want to study whether the "publish-subscribe" protocol may be more suited to your needs.

Your server will not be able to summon the callback routines of your client(s). It will need to send a message to the client, and then the client will do something... or maybe nothing. For the cases where the client will do nothing, one must ask why then is it receiving a message from the server?

Aquarius_Girl 03-09-2011 05:49 AM

Quote:

Originally Posted by dwhitney67 (Post 4284012)
Your server will not be able to summon the callback routines of your client(s).

Why do you say that? What's the use of callbacks then?

dwhitney67 03-09-2011 05:51 AM

Quote:

Originally Posted by Anisha Kaul (Post 4284019)
Why do you say that? What's the use of callbacks then?

You stated earlier that you have a Server, which is presumably a separate application, from the Client. The Client connects to the Server, sends a request or two, and then the Server replies... via a socket.

Please tell me how you intend to get the Server to summon the functions of a separate process?

Aquarius_Girl 03-09-2011 06:28 AM

Please feel free to beat me with a broom for confusing the matters.

Code:

Server:            ClientA:      ClientB:
baseClassA            baseClassA      baseClassA
derivedClassA          derivedClassB    derivedClassC

Each one of the derived classes have e.g. 10 different messages to handle.
When the message reaches to e.g ClientA, the callback class/vector declared in the
baseClassA calls the intended callback function of derivedClassB.

I hope I am making sense now.

and I am yet to study the publish-subscribe protocol you mentioned.
I'll take a look at that. Thank you for being patient.

johnsfine 03-09-2011 06:43 AM

Quote:

Originally Posted by Anisha Kaul (Post 4283862)
I meant the socket programming, servers and client programs. The base class is common to
both the server and the client programs.

I hope you have some basic understanding of what processes are and the significance of being in the same process vs. in a different process.

dwhitney67 already tried to point that out in his replies. But I'm not sure you understood.

Socket programming client/server implies the client and server are in different processes. In most socket programming designs, client and server are allowed to be on different computers. (You could have the client and server in the same process. But if your design depends on the client and server being in the same process, the use of sockets is silly).

Callbacks are a mechanism used when the client and service are in the same process.

Quote:

This code compiles if I remove the red statement from main (), the error which I am getting right now is due to lack of understanding (despite your efforts)
I think I'll skip this round of explaining why your guess at how to code something doesn't mean what you hoped it would mean. I don't mean to denigrate your effort, but your approach is wrong.

You need to get a clearer idea of what you're trying to do. Then you might also need to look at some simpler examples to get a clearer idea of what C++ lets you do.

Aquarius_Girl 03-09-2011 07:16 AM

John,

Are you saying all this after reading the post number 10 of mine? I thought I made myself clear there!

johnsfine 03-09-2011 07:28 AM

Quote:

Originally Posted by Anisha Kaul (Post 4284116)
Are you saying all this after reading the post number 10 of mine? I thought I made myself clear there!

Yes, I had read post 10. No I didn't think it clarified anything.

Where are the process boundaries in your design? What information flows between processes?

Code in one process cannot directly call code in another process. By various complicated methods code in one process might cause code in another process to be called. But I'm not going to construct an example for this thread.

Aquarius_Girl 03-09-2011 07:40 AM

Quote:

Originally Posted by johnsfine (Post 4284129)
Code in one process cannot directly call code in another process.

I "know" that processes are two separate identities, they don't share memories like threads.
Processes can call/create other processes by functions like system () and fork ().

But thats not what we want here, I said in post no. 10 that I was explaining the problem wrongly,
I drew the diagram stating the separate server and client processes having their own copies of the
base class. I made it clear in post 10 that when a message is received by the client, its base class
is supposed to call the callback function defined in the derived class (and right now we are talking
only about the client which has received the message)

I cannot explain anything more clearly than this.

Anyways, thanks for the help you offered till now.

johnsfine 03-09-2011 08:16 AM

So we are talking about one process. The client/server socket stuff is all a distraction (some other part of the project about which you'll probably have more questions, but not relevant to the current question).

Just to make really sure that is what you mean: That vector you were trying to create exists separately in each client process. It is not stored in the server process. Correct?

We are also talking about a parameter type and return type that are known at compile time and are the same for all callbacks within one client process.

I have done such things many times and always cheat. Cheating involves reinterpret casts to hide some of the polymorphism, combined with significant internal knowledge of possible methods the compiler has for implementing various features, combined with coding well outside the rules of the C++ standard (into undefined behavior) but with care to be sure the undefined behavior will be correct.

I don't intend to give any examples of the above. I haven't thought of any way to get the full efficiency of those reinterpret cast tricks without taking advantage of undefined behavior.

The only way I can think of at the moment to do what you want without using any undefined C++ behavior is with an extra level of wrapping combining templating with virtual functions.

First the main consequence of that design (as I described earlier) is that the vector cannot contain the actual callback objects, but must contain pointers to the callback objects. That in turn means you need extra care to explicitly delete each callback objects at the point you are done with it.

One clean object oriented design is:

1) Define a base class for all callback objects, that declares a pure virtual execute function and a virtual destructor.

2) Define a templated callback class whose base class is (1) and whose template parameter is the class of which the callback is a member. This templated class overloads the execute and destructor methods.

3) The new operator for (2) is called from code that knows (at compile time) the class of the callback and knows at run time the specific member of the callback. That code passes the pointer to object (2) cast as a pointer to object (1) to the owner of the vector.

4) The owner of the vector later calls the virtual execute function on a pointer to (1) which reaches the correct execute function of type (2) which calls the actual callback function. I expect that is also the point at which the destructor must be explicitly called.


All times are GMT -5. The time now is 08:31 AM.