Sign in to follow this  
fpsgamer

[C++ Design Advice] A scheduling library ...

Recommended Posts

Yet again I am tapping you guys for advice. I would like some input on the design of a library I would like to write: The purpose of the library is to 'schedule events'. You schedule an event by supplying: - The time the event is to occur - A pointer to a function to be called when the supplied time arrives. - The arguments to the above function. I have two main questions. 1) How can I add the ability to register callback functions that can take an arbitrary set of arguments. The arguments and their order are only known to the person registering the event, not the scheduling library. I've heard whispers about something in boost library that may help, though even if boost provides a full solution, I would like to know how it works for the sake of my learning. 2) My next question is about how the linker will be able to resolve locations of these functions when the main application is running.
               SchedulingLibrary.lib
                       |
       ----------------------------------
      |                |                 |
ClientLibraryA.lib ClientLibraryB.lib   MainApplication


I would like it to be such that the scheduling library doesn't have any external dependencies, so every other application component will link against it and not vice versa. But my question is that if for example: ClientLibraryA registers a callback defined in ClientLibraryB, does this mean that ClientLibraryA has to link against ClientLibraryB? What if ClientLibaryB specifies a callback in the main application? How can ClientLibraryB then link against the MainApplication? By the time the main application is running, how does the linker figure out where everything is? Thanks in advance :) [Edited by - fpsgamer on August 2, 2007 1:05:41 AM]

Share this post


Link to post
Share on other sites
The specific libraries you'll be interested in are boost::bind and boost::function, which provide a way to bind arguments to a function and store the resultant nullary functionoid for execution later. With proper use of these, there won't be any problem with linkage.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
The specific libraries you'll be interested in are boost::bind and boost::function, which provide a way to bind arguments to a function and store the resultant nullary functionoid for execution later. With proper use of these, there won't be any problem with linkage.


Thanks for the tip :).

Something I didn't mention earlier is the fact that I also want to save the callback information to a file so this information is preserved while the application is not running.

Will the addresses of the callback functions to remain valid over several invocations of the application? I've heard of randomized address spaces, do those actually exist in any major operating system implementations? Would they even affect the code segment?

Also I presume that if the binary is updated/patched that could certainly invalidate function pointers from a past invocation of the application.

Share this post


Link to post
Share on other sites
Quote:
Original post by fpsgamer
Quote:
Original post by Sneftel
The specific libraries you'll be interested in are boost::bind and boost::function, which provide a way to bind arguments to a function and store the resultant nullary functionoid for execution later. With proper use of these, there won't be any problem with linkage.


Thanks for the tip :).

Something I didn't mention earlier is the fact that I also want to save the callback information to a file so this information is preserved while the application is not running.

Will the addresses of the callback functions to remain valid over several invocations of the application? I've heard of randomized address spaces, do those actually exist in any major operating system implementations? Would they even affect the code segment?

Also I presume that if the binary is updated/patched that could certainly invalidate function pointers from a past invocation of the application.

Don't do that. Create a std::map() register each callback to the map together with the name, and store the name in your config file. On startup, read the config file, get the name, run through the std::map to get the function pointer and that's all.

As you said, a modified binary will result in modified address, so even if the process is loaded at the same virtual offset each time, there is no gaurantee that your function pointer will be at the same offset in memory.

HTH,

Share this post


Link to post
Share on other sites
for a second I'm going to assume you want to write a program that is portable to other platforms and works using pure standard C++ .... in which case it is fundamentally impossible to just serialize live program information like function pointers and argument values generically. You have to have some sort of object system in place for doing this serialization /deserialization etc. in a predictable manner.

First, your methods would need to register themselves - because there is no such thing are getting to function by either name or address that works in standard C++ on multiple compilers and platforms. But it is easy to do with a plugin / registration system.

Second the arguments are another issue, because I don't believe that boost::bind and boost::function are serializable / persistable.

You have to realize that saving something to a file to reload on a later run of the program is no different than sending it via email to someone to run on a different computer. It can be done if the program has a the proper infrastructure in place ... but not just magically via standard simple C++ methods.

Share this post


Link to post
Share on other sites
I have thought about this further and I have come to a number of a conclusions, but still have some remaining questions.

New conclusions:
----------------

1)
I will require the application to register callbacks every time at application starts. Like Emmanuel Deloget reccomended earlier, registered functions will be stored using an std::map which associates a function name (string) to a function/functor pointer.

2) I have decided for the sake of simplicity that callbacks will be required to take one argument. Maybe once I get a clearer picture of things I will be able to loosen this restriction. For now functions that require multiple arguments will have to pack them into a single class/struct.

2)

The prototype for scheduling an event will be


template < typename T>
schedule(time_t time, std::string functionName, T arg1);





- time is the time in seconds since the epoch that the event is to trigger
- functionName is the key in the hash table which maps to the function we want to call.
- arg1 is the value of the first argument to the function mapped by functionName
- T is be the type of the argument accepted by the function mapped to by functionName.


3) Each time a call to scheduleEvent() is made I will create one of the following structures and insert them into a priority queue:


template <typename S>
class PQElement{
time_t time;
std::string functionName;
S arg;
};





The priority queue will ensure the PQElement's will be ordered by time.


Remaining Issues:
------------------

1) When the application closes we need to save all the PQElements in our priority queue to disk so we do not lose all the events that have been scheduled so far.

The problem becomes resolving the template type 'S' for each PQElement when we re-open the file.

Note: The following is my current rough understanding of the situation:
I only care about the size of 'S' not its type. I think I can just dump a chunk of data the size of 'S' into the callback functions first argument and have it work without knowing the type.

How does my reasoning sound. Have I overlooked anything. Also any implementation tips (especially on the unresolved part) would be much appreciated.

[edit]

As an aside qestion: Is there a hard limit on the number of arguments that can be passed to a function?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this