
Christopher Kohlhoff wrote:
One item that I have difficulty in discerning a clear design/rationale for is the demuxer. The demuxer as implemented is a repository of services (another wooly concept afaiks) and something that provides the run, interrupt and related functions to control the scheduling/dispatching of handlers in reaction to changes in Async_Object state. Is there anything preventing a more policy-based approach to assembling services?
The demuxer is composed of services in a similar way to how std::locale is composed of facets.
That tells me that there services are objects identified by type that can be obtained from the thing that holds them (locale in iostreams, demuxer in asio) by type. Which is - um - about what I knew already....
The demuxer is not necessarily aware of, or coupled to, the service types that it contains. For example, the SSL implementation uses a service that is only added in to the demuxer if SSL is used.
I gathered that much. But tell me why/what for! Alternatively I'll try to figure it out and write the docs - you can tell me if I get them right... [pause to read code in more detail] Ok - there are a number of service categories and some services depend on other services. The stream_socket_service impl may, for example, be a reactive_socket_service. The reactive_socket_service ultimately needs a demuxer and a reactor, so it gets the reactor (itself a service) from the demuxer via get_service. It is documented that (unlike facets of a locale) the services are constructed on use. This means that there is no way (?) that a custom replacement service can be provided via the demuxer, as the actual construction of the service is done by the service_factory based on the type of service requested from the demuxer. Hence, to customize the service, it has to have a different type from any existing service, and the user of the service has to be modified to use the new type. There is a possibility that some services are used by multiple components (eg the demuxer and the socket) so care is needed in doing this customization. Basically about as unlike a local and facets as you can get? I think this is what threw me to start with. Did I get that right? Having been completely mislead by the docs, I think the real model is something like this: Having obtained the "locale" in the form of the demultiplexer the "socket" can look up services that it needs by type. Thats fine, I supposed initially that this meant a socket would look up something that actually was a concrete implementation of a documented concept, but I found that it looked up a blob-o-stuff documented as a service. As it happens that blob-o-stuff may depend on detail impl that uses detail services, some of which look suspiciously like they do implement the concepts. I'm left feeling that aside from impl detail this is all of no significance except that service instances are 1:1 with demuxer instances so it is a demuxer extension mechanism that is handy if a socket policy needs to associate state with a demuxer not a socket instance. As a special case the demuxing itself is accessed as a service. In the case of policies that are associated with (or include a component associated/implementing) the socket, variations on an idiom of using a service::null function to get a null impl (ptr) and some form of create/open etc to c'truct the actual impl is used. What I was asking for (vaguely) was that the services be more facet-like so that when customizing a socket-like class it can be assembled from appropriate facets/concepts. This might mean pulling some of the detail out of detail (leaving the platform dependent parts there). Alternatively composite services could be assembled (as appears to be the case in detail already) from their basic concepts. In any case, I think a better description of what the service concept is all about (possibly more than 1 concept here - the core service access, services that use the demuxer and demuxer services) are needed if this is to be usable as an accessible customization point of the library.
The service used by a particular public interface (e.g. basic_stream_socket) is already a policy template parameter. One example program shows how basic_stream_socket can be instantiated with a custom service to perform logging, where this service is in turn implemented using the standard stream_socket_service.
I've had a look at that now. Is there some particularly profound reason why logging implemented to fit in with the service idiom is a good thing/example? It seems to achieve nothing special, though it goes through some hoops to create a demuxer (private to the logging service) to perform work in a background thread? Is that supposed to illustrate something? I think this relates back to the need for better docs/concepts in this area. The replacement stream_socket_service is a copy of the original's interface with logging calls added. It wraps (has a reference to) an implementation that is the original asio::stream_socket_service. I agree this illustrates some of the techniques I was asking about should be practical within the design, which addresses some of my concerns. I'm happy (unless you can tell me where I've got it completely wrong!) I understand this area well enough for now. I would want see a much better level of docs and examples of this area in a post-review version of the lib. Regards Darryl Green. -- No virus found in this outgoing message. Checked by AVG Free Edition. Version: 7.1.371 / Virus Database: 267.14.7/214 - Release Date: 23/12/2005