[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

XTL patches, problems, features

Hi XTLers,
I recently used XTL in a project (a computer game on Win32) 
and had to do quite a lot of adjustments to make XTL work 
the way I wanted.

Some of the stuff concerns compiler specific problems
(MS VC++ 6.1), which you probably want to include in the 
main distribution. Other things are new features (making 
auto_object work in deeper nested calls; using iostreams as 
output etc.). With some software design issues I'm not so 
happy - decide want you want to include in the distribution.

Most of the changes I did in the auto_obj_input/output 
classes since I only used these. Some of them would be 
relevant to obj_*put as well.

1) The XTL_COMPOSITE_BUG. In one of the earlier patches to 
XTL1.3 a problem with template specialization with the MSVC 
compiler was fixed by introducing some compiler defines and 
workarounds. They are generally not neccessary at the point 
they were used - since MSVC is able to handle template 
specialization. The only requirement is that the 
specialized function comes AFTER the more general one (for 
whatever reason...). In the obj_input/output classes the 

  template <class T>
  inline obj_input& simple(T& data) {...}

needs to be above the specialized functions, declared by


If there is no problem with this order in other compilers, 
than the XTL_COMPOSITE_BUG define can be removed.

2) But there is another problem with template 
specialization in MSVC. The compiler does not regard a 
template with just one parameter to be more specialized 
than a template with two parameters.
This functionality is used in XTL to enable the 
serialization of third-party classes. The global-scope 

  template <class Stream, class T>
  inline void composite(Stream& stream, T& data) {...}

is not regarded as a less specialized function than

  template <class Stream>
  inline void composite(Stream& stream, MyType& data) {...}

There are two ways to work around this. Either you write 
two or more functions without any function template for 
every stream you have, e.g.

  inline void composite(obj_input& stream, MyType& data);
  inline void composite(obj_output& stream, MyType& data);

or you could use a different function in the streams to 
serialize third-party classes. I chose the latter approach, 
because I did not want the code-doubling of the first (Can 
anyone think of compiler macros or whatever to work around 
this?). I introduced to the auto_obj_*put classes a method 
called special() that calls a global-scope function called 
special for the data type of its parameter. The 
disadvantage of this approach, is that the third-party data 
types cannot be serialized by other methods like array() or 
vector() any more. As an example I added a special_array() 
method to serialize arrays of third-party data types, which 
I had a lot of.

3) A small type check problem: In graphio.h there is a map 
to translate the graph indices to pointers and vice versa.
The type was defined as

  typedef std::map<void*,int> trans_type;

but is used as a map in both directions, sometimes filled 
with an int as key and a void* as second value. I got some 
error messages with my compiler settings, and therefore 
split the type into two, one with a map for input and one 
for output operations.

4) I had quite a big problem with getting auto_object_*put 
to really work. Has it ever been tested on a real class 
hierarchy? The problem is that it works fine, if you call 
auto_object() from the main application, for a stream 
variable you declared there. But when you serialize a whole 
tree of classes, the type of the stream (=auto_obj_*put) is 
lost when any of the inherited methods in obj_*put is used 
for serializing the first level of the object hierarchy. 
The obj_*put methods basically do an implicit up-cast with 
the stream variable, and I don't see an elegant way to 
down-cast it to an auto_obj_*put type without the ability 
to use virtual methods on templates.

The very ugly way I worked around this is by copying all 
the serialization methods from the obj_*put classes to the 
auto_obj_*put classes and changing all the stream types 
used by them to auto_obj_*put. If anyone can think of a 
better way, please let me know. There is the remote 
possibility that the problem just exists with the MSVC 
compiler, but I don't think so.

5) I needed a graph reference system like the one in 
graphio.h that works with the auto_obj_*put classes. The 
problem here is that the classes cannot be templates that 
use the reference class as a parameter.

Instead I introduced a small interface with a class 
heritance which was supposed to be exchangeble just by 
standard dynamic linkage - so that you still could choose 
between a no-reference and a graph-reference system or 
whatever. I didn't really get it to work and didn't care 
that much after the graph system worked. But the classes 
are all there and you could choose something else than 
graph references by changing the type of the refs member 
variable in the auto_obj_*put classes.

Note that the auto_object() function automatically uses the 
reference system, just like the reference() function. In 
case you don't want the overhead of this, you can use 
auto_object_noref() instead.

6) Another method I added to the auto_obj_*put classes is 
object_container(), which works like container(), but calls 
auto_object() instead of simple() on any of the items in 
the container.

7) Something that I haven't changed yet, but thought about 
doing it: I don't really like the idea of having to build 
the whole externalizable_index in one source file, instead 
of declaring every class in its own source. A few 
experiments with this showed that there is a problem with 
the order of execution - since you don't have any control 
over the order the initialization code of the source files 
is executed.
I think this problem might be avoided by using a singleton 
pattern for the externalizable_index variable instead of a 
global variable, but I didn't have time to try it out yet.

8) Two really minor things:
I renamed the reader classes in 
autoio.h. I changed it to "creator" and similar, because 
they don't really read anything.

I added a few empty method bodies to the text_format class, 
because it didn't compile for a test without them.

9) And finally: I did it - a simple system of buffer classes 
that work with C++ iostreams - just to help you with the 
once promised feature of being able to write to C++ 
streams... The file is called iostrbuf.h

That's all. If you're still reading this, you're probably 
still interested in XTL, and I hope you don't hate me for 
ruining your piece of delicate software artwork :-)

I really like XTL and appreciate your effort in its 
development. Thank you.

Tell me what you think about my changes,
   Lutz Latta

Lutz Latta
Email: lutz@latta.de
"University of the West of England"