Stream Factory Interface (Question of Preference)

This is a discussion on Stream Factory Interface (Question of Preference) within the C++ Programming forums, part of the General Programming Boards category; I seem to be stuck staring at the trees...
Background: I'm playing around with some library code for loading images ...

Stream Factory Interface (Question of Preference)

I seem to be stuck staring at the trees...

Background: I'm playing around with some library code for loading images and thought I'd ask what interface others would proffer. Here I have a stream 'in', say 'std::istream' for simplicity, and desire a rather direct creation interface, but find everything... lacking. (Basically, which feels right in your opinion?)

1):

Code:

smart_pointer<some_class> whatever(factory(in));

2):

Code:

smart_pointer<some_class> whatever(in >> factory());

3):

Code:

smart_pointer<some_class> whatever(0);
factory(whatever, in));

Even something as simply as '1' would be sufficient and helpful.

(Note: If I've missed an option, and I rather hope I have, then I welcome any comments regarding such other options.)

Definitely not #2. The stream extraction operator returns the stream by convention, and breaking this would only lead to chaos.
I'm for #1. A function that takes a stream and extracts returns a pointer to a newly initialized object seems just right.

The stream extraction operator returns the stream by convention, and breaking this would only lead to chaos.

Well... I had two more options originally, but the syntax quickly went ugly. There is nothing that says this can't support chaining, but it would get ugly. Perhaps we should call this one a miss as well.

A function that takes a stream and extracts returns a pointer to a newly initialized object seems just right.

Yep, that seems to be the consensus, but either output seems... wrong. I have a thing for symmetry and consistency. (Replace the name "factory" with whatever you like or add a '*' wherever you wish; it still seems wrong. The "factory" could even be a different type if you desire.)

Code:

factory(out) << whatever;

Code:

out << factory(whatever);

Yea, that probably needs explanation. Usually the objects themselves, once an instance is obtained, have no problem "writing". However, there is no good way of manipulating the wide variety of encoding options of the various image formats using only an abstract interface. (The best, I fear, portable option would be hiding a variety of dumb strings like "jpeg-encoder: --arithmetic-coding" behind objects inheriting from some base.) And, worse in my opinion, this would make the serialization mechanics for these classes inconsistent with every other class. It slightly complicates matters, but I think having an external machine to do external decoration, like huffman versus arithmetic encoding, is the correct approach.

I'd hide the factory implementation behind the library's interface.

Er... that's neither here nor there. I'm asking about the interface. From the snippet you posted it seems you're suggestion would be number '1' as well, but it also seems as if you are saying that the name of a file should be passed to the interface. If so, that isn't an option; nothing is gained by limiting the interface in that way. (True, a URL prefix like 'pipe://', 'file://', 'udp://' could be used, but even still, you only lose capabilities by passing a string.)

Despite the strange novelty, I think "decorating" the stream would be the best solution in general with an option of '1' for constructor use.

Er... that's neither here nor there. I'm asking about the interface. From the snippet you posted it seems you're suggestion would be number '1' as well, but it also seems as if you are saying that the name of a file should be passed to the interface. If so, that isn't an option; nothing is gained by limiting the interface in that way. (True, a URL prefix like 'pipe://', 'file://', 'udp://' could be used, but even still, you only lose capabilities by passing a string.)

Maybe I don't understand the interface you're defining. Any image loading library I've used (PIL, SDL, etc.) return a pointer to a loaded image or surface object given a file name. How the image is loaded isn't my concern. Sounds maybe that you're defining something that an SDL or PIL might themselves use.

Maybe I don't understand the interface you're defining. Any image loading library I've used (PIL, SDL, etc.) return a pointer to a loaded image or surface object given a file name. How the image is loaded isn't my concern. Sounds maybe that you're defining something that an SDL or PIL might themselves use.

Well, I suppose it may be that you don't care how an image is loaded so long as it is loaded correctly, but if you save that image back to a file you probably do care because, for example, certain options may not be supported correctly by some applications.

Anyway, I think maybe the only thing you didn't understand was which bit I said wasn't an option. I have no problem with a specific interface taking a file name and returning a pointer to some object representing the data contained in that file. I just have no intention of writing one. (I mean, such convenience functions are, to me, an uninteresting and different area of decoration.) With a tiny bit of extra effort the primary interface can provide greater functionality without sacrificing any, in my opinion, usability.

I think some source using the constructor mechanic may help. (The names aren't really relevant. I just copied and pasted.)

There is nothing mechanically or logically wrong with your offering:

Code:

smart_pointer<some_class> whatever(factory("file.png"));

The only thing is, there is no reason to restrict the primary interface and no reason to provide additional, possibly conflicting, interfaces for non-file or non-local images. (I have no problem with convenience functions, but they don't represent an actual part of the library interface.) The primary interface as I'm providing, regardless of the final syntactical form, takes a variant of a bit stream. The difference is hardly noticeable if all you want to do is open a file:

Sorry if I'm being dense, but I've only used image loading in the context of games/graphics and loading from files. Perhaps if you could give an example of what some_class, whatever, and factory might represent in an application, I could get some context here.

I don't really understand why, but sure, why not. (Note: The following is a discussion of how things currently exist. For almost two years I've been working on cleaning up the vast majority of code I've written over the last thirteen years so that I could publish it with the modest hope that it might be useful and educational.)

They are all just variable names of course. The types are the important bit.

smart_pointer): A fairly generic smart pointer implementation using my variant of policy based design. (My variant is a bit more portable and complete--in that the policy classes support, for example, multiple virtual inheritance and are themselves derived through other policies and traits.)

some_class): The type representing the abstract interface for images, surfaces, and surface manipulation. It describes the interface for several vector and bitmap drawing methods. (Yes, it is a derived from a template, but that isn't important and is only implemented in this way because the default implementation of certain meta-functions "knows" how to obtain an interface in this way.)

Code:

template
<
typename dictionary_T
>
surface<interface, dictionary_T>;

factory): An instance of the class derived by instantiating a factory generator--a template class. I do not write factories that know how to process more than one type. This is basically an instance of the type that results from gluing several of such tiny factories together.

Code:

template
<
typename employee_T,
typename dictionary_T
>
factory;

whatever): An instance of 'reference<surface<interface>::type>::type'.

Now then, apparently your getting confused because of the word involved and the existence of the "factory pattern". (Sorry if you aren't, but that seems to be the case.) The variable 'factory' as in my example is a candidate for the factory pattern, but that isn't really the intent, and possibly I should attempt to find a more suitable name--or at least one with fewer collisions. The interface of the factories I write do spawn instances of objects, but, by and large, they perform processing the objects themselves aren't capable of to support formal input and output. (They, the objects themselves, could be capable of such processing; I just don't like the design that results.)

Virtually every object in my library is capable of "simple" input and output. (Every object knows how to serialize and deserialize themselves using whatever dictionary and format--binary or text--is associated with the current stream.) This, for example, means that 'std::cout << *my_image' in one application piped to 'std::cin >> *my_image' in another application should result in the second application having a nice copy of '*my_image'. However, such source does not spawn any object; the source for the input application must know exactly what dictionary and format needs to be associated with the stream and what kind of object is to be expected. This is sufficient for a great many applications, but not nearly all of them.

For one, this simple model isn't sufficient for any purposes involving third-party applications. For example, there are a handful of CLI imaging utilities that can operate on named pipes, but can't do anything with the input my library would give it. My library obviously needs to provide a mechanism to process the objects for such formal purposes. This is where my factories come in; yes, they provide a mechanism to spawn new objects as expected, but they also provide processing on the output side of the equation.