ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
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).
Yes, that was my fault, sockets, clients and servers have NOTHING to do with callbacks. Thanks to dWhitney for making me realize that in his post 9.
Quote:
Originally Posted by johnsfine
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.
The vector is stored in the base class, throw out the client/servers out of this thread now and yes the return type and the parameter type are not a cause of worry since the return type is void and parameter is unsigned short.
Quote:
Originally Posted by johnsfine
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.
With the help of the people on some other forum, this code is compiling now:
But still there are syntaxes in this code which I am yet to understand. Templates suck. I don't understand when I push the values in the vector in the derived class, why they don't get reflected in the base class?
I think the problem is that I should call the the baseclass constructor from the derived class constructor, like this: AClass () : BaseClass ()
You have BaseClass templated on MyDummyClass. I don't understand how that can fit your needs.
That means an instance of that vector exists for only one real class substituted for MyDummyClass. I thought you wanted one vector for all the callbacks in the process and you wanted different classes containing the member functions that are called. That was the whole reason any complicated templating was required.
Quote:
Originally Posted by Anisha Kaul
I don't understand when I push the values in the vector in the derived class, why they don't get reflected in the base class?
I'm sure they do. You don't seem to have tested whether they do. I think you might be confused by the sequence of events:
During the construction of a an object, the base class constructor is called before the member constructors, which are called before the body.
In the body of a constructor, you pushed a value into a vector in the base class. In the constructor of the base class, you check whether that value has been pushed. But the base class constructor was executed first, before the value could be pushed.
Quote:
I think the problem is that I should call the the baseclass constructor from the derived class constructor, like this: AClass () : BaseClass ()
That changes nothing. The base class constructor is always called and always called at the start of construction of the object. With an explicit call to the base class constructor you can change which base class constructor is called (if it has more than one) and you can provide parameters for the base class constructor. But you can't change the sequence. The only way to do work before the base class constructor is to put that work into the expression that computes a parameter value for the base class constructor (and that trick has some complications of its own).
Quote:
Originally Posted by Anisha Kaul
Templates suck.
Templates are incredibly flexible. Flexibility can be confusing.
Apparently with someone else's help, you have used one of the strangest flexibilities of C++ templating: AClass derives from BaseClass but BaseClass is templated on AClass. That circular relationship is initially quite confusing. It is a very powerful technique in C++ programming. But I'm not convinced it is an appropriate technique for your problem.
Quote:
The vector is stored in the base class
Loose terminology there may be masking a genuine confusion. The vector is not stored in the base class. A vector is stored in each object of base class type. Every object of the derived type(s) has an object of base class type inside it.
So you have one vector per derived object. Is that what you want? Or one per derived type? Or one shared by all objects of all derived types within the process?
You have BaseClass templated on MyDummyClass. I don't understand how that can fit your needs.
Neither do I, the templates are the first thing in my life that I am finding so much difficult to understand
Quote:
Originally Posted by johnsfine
That means an instance of that vector exists for only one real class substituted for MyDummyClass. I thought you wanted one vector for all the callbacks in the process and you wanted different classes containing the member functions that are called. That was the whole reason any complicated templating was required.
Let me read up the thinking in c++ II, callbacks are explained there.
Quote:
Originally Posted by johnsfine
I'm sure they do. You don't seem to have tested whether they do. I think you might be confused by the sequence of events:
Thanks for pointing out, I did execute the code before posting here, but now I do realize I was getting confused by the sequence of events, let me see how to fix that.
Can you describe the desired sequence of events from the point of view of the callback system?
I don't understand why you want to be creating or executing callbacks in the AClass construction. I don't understand why the owner of the callback vector ought to be a baseclass of AClass.
Not yet totally ignoring the client/server "distraction", I would expect that execution of callbacks would be triggered by events from outside this process, so from very much outside objects in this class hierarchy.
I'm not good at explaining, so you'll have to take this apart:
Code:
#include <string>
#include <iostream>
#include <vector>
#include <exception>
//using namespace std; <<-- DON'T do this, brings a lot of stuff into global namespace
template <class MyDummyClass, typename ReturnType, typename Parameter> class SingularCallBack{
public:
//Don't mix tabs and spaces.
typedef ReturnType (MyDummyClass::*Method)(Parameter);
//
/*SingularCallBack(MyDummyClass* _class_instance, Method _method){ <-- variable names shoudl not begin with underscore - those are reserved
If you REALLY want underscore, try putting it at the end of the name*/
SingularCallBack(MyDummyClass* classInstance_, Method method_)
:classInstance(classInstance_), method(method_){//<<-- don't use assignments, when you can use initializer list
/*class_instance = classInstance_;
method = method_;*/
};
ReturnType execute(Parameter parameter){//you could use operator()
return (classInstance->*method)(parameter);
};
inline ReturnType operator()(Parameter parameter){
return execute(parameter);
}
private:
MyDummyClass* classInstance;
Method method;
};
class BaseClass{
public:
virtual bool DerivedMethod (const std::string& str){
return true;
}
};
#if 0// won't work, disabled
//class AClass : public BaseClass{
//public:
/*template<class derivedComponent, class Allocator = allocator<derivedComponent>> class callbackList;*/
// ^^^ nope. It isn't a variable, it is a type. You clealry want variable. You can't (AFAIK) make template variables, only functions.
// AClass(){} <-- nothing to initialize, no need for construtor, unless you want to support assignment operators and such.
// bool AMethod( std::string& str){
// std::cout << "AClass[]: " << str << std::endl;
// return true;
// }
//};
#endif
class OutOfRange: public std::exception{
public:
virtual const char* what() const throw(){
return "array index out of range";
}
};
template <class Class, typename Result, typename Argument> class AClass{
public:
typedef SingularCallBack<Class, Result, Argument> Callback;
protected:
typedef std::vector<Callback> CallbackList;
CallbackList callbackList;
public:
size_t size() const{
return callbackList.size();
}
void clear(){
callbackList.clear();
}
void addCallback(Class* classInstance, typename Callback::Method method){
callbackList.push_back(Callback(classInstance, method));
}
Result runCallback(size_t index, Argument argument){
if (index > size())
throw OutOfRange();
Callback& cur = callbackList[index];
return cur(argument);
}
};
class TestClass{
public:
bool doIt(std::string arg){//technically, we need to use const std::string& here.
std::cout << arg << std::endl;
return true;
}
};
int main(int argc, char** argv){
TestClass test;
AClass<TestClass, bool, std::string> callbacks;
callbacks.addCallback(&test, &TestClass::doIt);
callbacks.runCallback(0, "In my restless dreams, I see that town.");
//a.callbackList.push_back (SingularCallBack < AClass, bool, const std::string& > (&a, &AClass :: AMethod, "sadsa"));
//^^^You will need two different classes here, otherwise it will be impossible to declare aclass.
return 0;
}
For more complex scenarios, where callback list stores multiple different types, you'll need to make an std::list of smart pointers (if you wish to sort the list, etc), where template class will be derived from an abstract non-template base class. And that would be beyond amount of time I can/wish to spare.
I'll try to find some time to read that later. But meanwhile, I wrote some code for you that demonstrates a simpler approach to what I think you're trying to accomplish:
Code:
#include <iostream>
#include <vector>
typedef int ResultType;
typedef int ParameterType;
struct BaseCallBack {
virtual ~BaseCallBack() {}
virtual ResultType execute( ParameterType ) = 0;
};
template <class Object>
struct CallBack : public BaseCallBack {
typedef ResultType (Object::* Fun) ( ParameterType );
Object& m_object;
Fun m_fun;
CallBack( Object& object, Fun fun ) :
m_object( object ),
m_fun( fun )
{}
~CallBack() {}
ResultType execute( ParameterType p) {
return (m_object.*m_fun)( p );
}
};
struct CallBackManager // No objects of this type. All members are static
{
static std::vector<BaseCallBack*> call_backs;
template <class A, class F>
static void add( A& a, F f ) {
call_backs.push_back( new CallBack<A>(a, f) );
}
static ResultType execute_next( ParameterType p ) {
ResultType result = call_backs[0]->execute( p );
delete call_backs[0];
call_backs.erase( call_backs.begin() );
return result;
}
};
std::vector<BaseCallBack*> CallBackManager::call_backs;
struct AClass {
ResultType fred( ParameterType p ) {
std::cout << "AClass::fred was called with parameter " << p << std::endl;
return 1;
}
};
struct BClass {
ResultType george( ParameterType p ) {
std::cout << "BClass::george was called with parameter " << p << std::endl;
return 2;
}
};
int main()
{
AClass a;
BClass b;
CallBackManager::add( a, &AClass::fred );
CallBackManager::add( b, &BClass::george );
std::cout << "Finished adding" << std::endl;
ResultType r = CallBackManager::execute_next( 11 );
std::cout << "First execute returned " << r << std::endl;
r = CallBackManager::execute_next( 12 );
std::cout << "Second execute returned " << r << std::endl;
return 0;
}
I'll try to find some time to read that later. But meanwhile, I wrote some code for you that demonstrates a simpler approach to what I think you're trying to accomplish:
Dude, you have a memory leak.
call_backs in CallBackManager is static, it creates objects with new, and doesn't free the memory.
THIS is why I said you'll need smart pointers - in C++ it is generally recommended to make sure that all objects are release automatically (i.e. when program nukes std::vector, it should release every element without extra effort from the programmer). Smart pointers make this happen.
Your idea is right overall, but with static members and without smartpointers it is a "no go".
(i.e. when program nukes std::vector, it should release every element without extra effort from the programmer).
The program doesn't "nuke" the vector. You're effectively saying that if there were a bug, that would also cause a memory leak.
Most of my code assumes the rest of my code is correct. Coding defensively has benefits, but also costs. I choose to code defensively only when I expect the benefits to outweigh the costs.
I'll try to find some time to read that later. But meanwhile, I wrote some code for you
I somehow failed to notice who wrote post #20 and thought it was Anisha's example of what was required rather than SigTerm's guess at what was required combined with suggestion of how to do it, as my post #21 is my guess at what was required and suggestion of how to do it.
So I read the code in post #20, and don't get the point, but I'd rather not discuss it. Unless Anisha answers some of the dangling questions about requirements there is little purpose served by guessing which requirements fail to be met by a design.
The memory is freed when execute_next() is called.
This is not something I would recommended. If execute_next() is not called, then memory will not be freed.
Quote:
Originally Posted by johnsfine
The program doesn't "nuke" the vector.
The vector is being nuked automatically when program reaches certain point.
The vector destructor is called eventually, which is what I meant by "program nukes the vector".
Quote:
Originally Posted by johnsfine
Most of my code assumes the rest of my code is correct.
I would not call smartpointer approach defensive, and I do not think this your approach is safe (murphy's law), but I guess everybody has a coding preferences.
IMO, making a CallbackManager non-static (or into a singleton) and providing a destructor would be safer.
However, discussing coding styles is rather pointless (people don't even agree about bracket position...), so I won't continue.
This is not something I would recommended. If execute_next() is not called, then memory will not be freed.
I cannot speak for all systems, but for most Linux systems the memory will be recouped when the application exits.
Quote:
Originally Posted by SigTerm
... providing a destructor would be safer.
Agreed, but what you mean by "safer"? Without the destructor, is there a risk that the computer will blow up, thus making for an un-safe working environment?
I cannot speak for all systems, but for most Linux systems the memory will be recouped when the application exits.
It will be released on windows platform as well, but it doesn't mean you should forget about memory management.
Quote:
Originally Posted by dwhitney67
Agreed, but what you mean by "safer"?
When you provide destructor, you have a warranty that object gets destroyed when execution leaves scope. It will also work with exceptions - you throw exception, and everything gets released (that's the main reason for using smartPointers). In comparison, memory allocated with new will not be release automatically, until program terminates. Not that "exception" doesn't mean program will terminate - C++ exception may be used to report about some non-fatal problem, in this case it'll be caught somewhere, and execution will resume, but memory won't be released automatically unless you used smart pointers, auto_ptr or something like that (depends on situation auto_ptr doesn't support copying/assignment).
indirect/hidden deallocation of memory is a bad programming practice that will most likely lead to bugs (some of them may be hard to track) and may turn into habit. If you're serious about programming (it isn't just a hobby you do for fun), then you shouldn't do something like this.
If program seems to work fine and doesn't blow up computer, it doesn't mean you can forget about memory management.
By using techniques like this, it is very easy to start writing apps that slowly eat all available system resources and then crashes. Example is here(interesting example, horrible memory management, eats all resources and crashes in 5 minutes on 2GB RAM system). It is easier to prevent such bugs in first place instead of thinking that "all your code is correct" (According to Murphy's law, it is not).
Another issue would be assignment operators and copy constructors.
Let's assume CallBackManager is not a static class and has destructor.
With no copy constructor implemented, this code:
Code:
CallBackManager a;
///initialized and filled with method.
{
CallBackManager b(a);
CallBackManager c = a;
}
//do something with a's pointers.
Will lead to segfault/access_violation - because memory will be released twice.
SmartPointers deal with this issue. Raw pointers don't, so you'll have to implement copy constructor and assignment operators (which will require extra work if list stores pointer to base abstract class), or make them private and forbid such operations.
IMO, making a CallbackManager non-static (or into a singleton)
I'm assuming (pending an answer from Anisha) that a design requirement is a single callback vector containing callbacks from multiple classes. If that is the requirement, how could the CallbackManager implementation be non-static other than as a singleton? I expect you would be even less satisfied with it as a namespace containing global members instead of a class containing static members.
In real programming I usually use a singleton instead of a class of static members. Sometimes a singleton is better. Sometimes I just use it out of habit.
In a sample program in a forum to demonstrate a technique, I do not use my typical coding standards nor style. A class of static members is easier to understand in a small example than a singleton. If there is a reason to use a singleton instead, any competent C++ programmer should be able to make that change. The other concepts of the example remain.
The unsafe aspect of my sample code was the choice to use Object& rather than Object* within the call back construction.
Conceptually, I think that should be a reference not a pointer, because the identity (address) of the selected object is permanently established at the time the callback is constructed.
But practically, it is too easy in a series of nested calls that ought to be by reference to accidentally make a call by value and invoke the copy constructor. If a sequence of calls is by object pointer, a mistaken call by object value is easily detected by the compiler and the error easily understood by the programmer.
In my own code, if I have layered calls that need to be by reference (would be wrong if by value) I do some defensive programming to make the mistakes detectable. Lots of my larger objects have private (and/or totally unusable) copy constructors anyway, because I don't like any implicit copy operations on large objects.
As for smart pointers, I'm more likely to trust my own ability to mange object lifetimes accurately.
Also, I don't worry much about inconsequential memory leaks. When a process exits, all the memory is released. Internal leaks cease to matter at that point. If an object ought to be deleted early enough in the execution of a program that significant new allocations will follow that point, then failure to delete it is a real memory leak. If an object "ought to be deleted" only immediately before the process exits, it doesn't actually matter (as a memory leak) whether it is deleted.
As for smart pointers, I'm more likely to trust my own ability to mange object lifetimes accurately.
Also, I don't worry much about inconsequential memory leaks. When a process exits, all the memory is released.
I assume that because I'm a human (well, I was a human last time I checked) I will make mistakes and it is unavoidable (murphy's law again). As a result, I will offload all work I can onto compiler, which starts with memory management.
The logic here is that machine has consistent behavior and won't forget to destroy something randomly. Another problem is that (IMO) manually managing memory and trusting yourself is not going to work when you, say, have a 1 megabyte of code you've been writing and maintaining for last 6 months - in this situation you'll be already busy with class hierarchy problems (who should inherit what and why), and various brain-wracking program design questions (how to add unexpected feature X that doesn't fit in model used by the program and require major rewrite of the half othe code), and "how the heck does this work" situations, so manual management will only make things more complicated. But that's just my opinion.
Anyway, there is a fundamental difference about (your/mine) coding style/way of thinking, so let's just stop. OP will gain nothing from this discussion.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.