|
Jason Stredwick
jason.stredwick@gmail.com Current Residence: Bothell, WA 98011 |
|
|
Summary:The Functor subsystem presented here: Functor.tar.gz is a extension of the Loki Functor by Andrei Alexandrescu and the Loki Library. I have taken the Functor and split it into its own entity under the namespace nFunctor. I have also split the Type headers into its own group under the namespace nType, (Type). I did this because the Functor depends on these header files. The rest of this page will give a brief overview of the components within nFunctor, a list of most of the changes made between this version and the Loki version, and the license this code falls under. The test code for this subsystem was successfully compiled and run on Visual Studio 2005, gcc4.0 (Linux/Flavor(?)), gcc3.2(Linux/Flavor(?)), and gcc3.3.1(SunOS 5.9). Also, to compile this code all dependencies are expect to reside at the same directory level as Functor. There is a Makefile for g++ and a solution file for Visual Studio. The solution resides in Functor/Functor/Functor.sln. DependenciesTypeOverview
The Functor subsystem is contained within the nFunctor namespace and contains
the following components (more info provide in header files):
-iFunctor- This provides the interface for all functors. Currently it allows
for up to and including fifteen parameter functors. Essentially, the
iFunctor provides cloning and execution.
-GenericFunctor- GenericFunctor is generic because it can take any member
function or callable entity in the constructor and forms a callable entity
itself. This class is generic because it can take a member function in
addition to all other callable types. However, in order to store all these
types, it loses type information about wrapped entity. This makes it
difficult to compare for equivalence with other functors including itself.
It also means that it has to create new objects, which is why it takes an
Allocation object as a template parameter. By default there is no special
allocation just the normal new and delete.
-Functor- This is an explicit version of GenericFunctor because it uses the
type of the callable entity as part of its type. This means that no object
creation is necessary for storage, and comparison for equivalence is
possible. The downside is the potentially long type declarations of
recursive functors. It also means that member functions must be wrapped in
a callable object before they can be stored in a Functor.
-MemberFunctionWrapper- This is the callable wrapper for member functions.
It provides the same interface as the other Functors and can be used on its
own. It inherits from iFunctor making it compatible with the other functor
types. This is also an explicit functor.
-ReturnlessFunctor- This class is intended to wrap any callable entity
and strip off its return value. This class would be useful for creating
lists of callable entities that share the same function input but don't
care about output. This functor is essentially, Functor, except it does
not return anything.
-ReturnlessMemFunWrap- This class is intended to wrap class methods
and strip off its return value. This class would be useful for creating
lists of callable entities that share the same function input but don't
care about output. This functor is essentially, MemberFunctionWrapper,
except it does not return anything. I found this class helpful when
trying to duplicate functionality from using ReturnlessFunctor because
I would otherwise have to create a series of intermediate instances to
hold the information to be passed to then be used by a ReturnlessFunctor.
-BindFirst- This a functor that takes other callable entites and a fixed value.
The fixed value is always given as the first parameter to the wrapped
entity. Thus, the BindFirst class must define its interface to have one
less arguement than the entity it wraps. This class also provides two
helper functions for creating a new BindFirst. The BindFirst is also an
explicit functor.
-Chain- This a functor that takes two callable entities and executes them
both upon the execution of the chain. Thus, you can create arbitrary
chains of execution by chaining together multiple Chain objects. This
class is also explicit and has two helper functions to that can create
new Chain objects.
The concept for this class makes it essentially a static chain. Once
the template parameters have been defined, to swap, add, or remove
links from the chain requires that the internal callable entity types
match up. Thus even though they can change, they can only change to other
like typed entities.
-OneToMany- This class is based on the Chain, but allows for a dynamic
chain. However, to do this forces a loss of generality in that it can only
handle functors and any class the derives for iFunctor. With this in mind,
it can keep a "list" of entities to execute. It can also manipulate that
list to add/remove/clear. Because this class has data to manage other than
an entity to wrap, it may need to handle threading at some point. That is
why it takes a StorageType as a template parameter. That way, the
programmer can decide how best to store and access the entities for
execution. Currently, the default StorageType is given in iStorageType.h
and only handles single threaded execution.
-ParmState- This is set up like a functor but instead of forwarding arguements
it stores them. It then provides a method that takes a functor to call
using the stored data. I have not figured out how I want to handle
pointers. Do I copy them or leave them the same? The point is to keep a
copy of the previous call, what if the data pointed to changes? I was
thinking of this class to be used in a delayed functor or a queuing
functor. A queuing functor may be something like a system resource like
the mouse gives off its position repeatedly before the wrapped entity is
prepared to receive it, but I want to process all of its movements.
Notes:
-Until gcc fixes their typename issues with templates such as the inability to
use "using typename" with inherited template names and the problem with
shadow hidden parameters, I had to leave the template arguement names as
typedefs even though they could be inherited. Supposedly it works on
Visual C++, Intel C++, and Comeau.
-Handling const operator() and entities - here are some notes from iFunctor:
* A few notes on why the operator()s are const and what that means for you.
* What do we see here? If you do not have const on the operator() for the
* functors, you can not wrap const callable entities. However, it seems
* that while const operator() allows for correct enforcement of calling
* const methods for const entities, it also allows for calling non-const
* methods on non-const entities. I did not think that the later was valid,
* but apparently it is. I am going to leave it now for two reasons. One
* I would have to add duplicates of all operator() for both a const and
* non-const version (how to handle volatile?), and 2) I did a quick test by
* adding both versions to iFunctor and Functor and it will call the
* appropriate version for a non-const Functor, but if the Functor itself is
* const we are right back at square one, calling non-const object's
* non-const member functions. Hopefully, someone more knowledgable than I
* will figure this out, otherwise I will try to come back at some point and
* figure it out. What would be nice is to make the functor's operator()s
* const/volatile reflect the wrapped entity's const/volatile settings.
* However this could be tricky for callable classes because you have to
* look at two places: the operator() and the constness of the class itself.
-By using PtrAccessibleType, all functors can now take callable entities by
value, reference, and pointer. Also see next note.
-To ensure validity of a functor, all entity pointers must dereferenced during
functor initialization to ensure a non-null value. From there on, all
functors assume non-null wrapped entities. Therefore, there is the
possibility of error due to dangling pointers, loss of scope on a given
reference, etc. However, I do not know a way to check for these other than
to suggest the use of some sort of smart pointers if you are worried.
-As far as threading is concerned, I feel it is up to the entities themselves
to ensure reentrance, not the functor. The functor is just a wrapper.
-One issue of threading that may need to be addressed is execution order. It is
one thing to allow reentrance, but what happens if you have two chains each
with a the same entity, how can you ensure the order in which entity is
executed in both chains? At at what level should this be taken care of?
ChangesThese are most of the changes that I can remember from the Loki version: Type -Broke type info headers into their own subsystem structure -I put all the type files under a new namespace nType -Added these headers to a new subsystem structure which includes some of my own headers for various things. -Added two new headers that are used by the functor classes to store their entities and bound/return types. These classes are PtrAccessibleType and ReassignableType. Functor -Moved Functor into its own subsystem structure -Broke up the Functor file into multiple files -Changed the namespace to nFunctor -Changed Functor to GenericFunctor, though I suppose I could change it back and call the new Functor class, ExplicitFunctor. -Created a new Functor which is a more explicit version of the generic one -Renamed the Impl stuff to iFunctor to signify that it is an interface rather than an implementation detail. -Changed iFunctor to be strictly an interface -Tried to change the inheritance of typedefs from iFunctor and iFunctorBase. However, it is mostly the same because I had to change it back when I discovered that gcc does not handle using typename correctly. I also had to change some template parameter names because gcc does not handle inheriting template names that are the same as the derived class's version. -Removed SmallObjectAllocation from GenericFunctor and added a generic Allocation template parameter instead. You can just pass in SmallObjectAlloc if you want. The allocation defaults to an empty class. -Moved comparisons out of the functor classes and made them non-member, non-friend functions. -Removed the RTTI stuff. All functors are compariable for equivalence except iFunctors and GenericFunctors. If we want to add RTTI comparisons for the functor types, it would be best if they were handled like the other comparisons, as non-member, non-friend functions. I may add them in later anyways, but just hadn't decided how I wanted to do it yet. The same goes for reverse cloning through iFunctor. -Changed all functors so they allow value, reference, and pointer versions of callable entities. -Pointer callable entities will throw a BadCallableEntity exception if the pointer is null. -Changed the methods for accessing the interface for all functors to casting, or two get methods, one for a pointer and one for a reference. -Added some comments -Added a test function for all the functors -Kept BinderFirst and Chainer in line with the other functors. They are now solely dependent on iFunctor. -Modified the BindFirst and Chain functions -Swapped the names of the Chainer class and Chain functions -Functor is similar to FunctionHandler class -MemberFunctionWrapper is based on the MemFunHandler class -Added ParmState which acts like a functor but stores the data internally, then provides an execution method that takes a callable entity and executes it with the saved data. This is essentially a work in progress. -Added OneToMany functor, which is similar to Chain but allows for dynamic chaining of callable entities. This class comes with the iStorageType.h supporting file. I am not sure if I want to keep iStorageType.h here or move it to another subsystem. Also, I don't like iStorageType and will probably change it in the near future. -Added ReturnlessMemFunWrap -I started calling the TypeList versions of things recursive and Seq versions linear, in the comments. -Added const to the operator()s to enable const entities and const methods. See documentation (iFunctor comments or FunctorInfo.txt) for more details. -Store data differently License//////////////////////////////////////////////////////////////////////////////// // The Loki Library // Copyright (c) 2001 by Andrei Alexandrescu // This code accompanies the book: // Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design // Patterns Applied". Copyright (c) 2001. Addison-Wesley. // Permission to use, copy, modify, distribute and sell this software for any // purpose is hereby granted without fee, provided that the above copyright // notice appear in all copies and that both that copyright notice and this // permission notice appear in supporting documentation. // The author or Addison-Wesley Longman make no representations about the // suitability of this software for any purpose. It is provided "as is" // without express or implied warranty. //////////////////////////////////////////////////////////////////////////////// |