From: John P. F. <jf...@ov...> - 2008-10-26 16:44:52
|
Dean Michael Berris wrote: > Hi John, > > On Fri, Oct 24, 2008 at 5:38 AM, John P. Feltz <jf...@ov...> wrote: > >> I implemented this in a similar network client project some time ago: >> >> namespace rfc3986 { >> template < >> typename ParserInput = std::string, >> typename Scheme = std::string, >> typename Authority = std::string, >> typename Path = std::string, >> typename Query = std::string, >> typename Fragment =std::string >> > class URI { >> public: >> > > We use traits for these things, and have a simple 'Tag' template argument. > > >> ///Public type access >> typedef Scheme scheme_type; >> typedef Authority authority_type; >> typedef Path path_type; >> typedef Query query_type; >> typedef Fragment fragment_type; >> >> > > These are unnecessary since the traits+tag will make the types > accessible even without having access to the definition of the class. > > >> Scheme getScheme() const { return m_scheme; } >> Authority getAuthority() const { return m_auth; } >> Path getPath() const { return m_path; } >> Query getQuery() const { return m_query; } >> Fragment getFragment() const { return m_fragment; } >> >> private: >> typedef URI< ParserInput, Scheme, Authority, Path, Query, >> Fragment> Output; >> >> friend Output* parse< ParserInput, Output> >> (std::stack<std::string>& errors, const ParserInput& input); >> >> > > Why a friend class for the parsing? > > One of my goals with this design was to provide the contractual guarantee of a valid URI representation. Allowing arbitrary construction wouldn't provide that. The only other alternative I'm aware of is to use ctor originating exceptions; there are several reasons why I chose not to do that, some of which I will discuss below. >> URI(Scheme scheme, Authority auth, Path path, Query query, >> Fragment fragment) : >> m_scheme(scheme), m_auth(auth), m_path(path), m_query(query), >> m_fragment(fragment) {} >> >> const Scheme m_scheme; >> const Authority m_auth; >> const Path m_path; >> const Query m_query; >> const Fragment m_fragment; >> }; >> }; >> >> > > There's no way to create a URI object from a constructor? > In this case, arbitrary construction is mis-construction, unless exceptions are deployed. I find the use of exceptions here problematic. For one it says that an invalid URI string is an exceptional condition -if parsing is what you intend to do. It also says that it is the clients responsibility to validate the input prior to construction of a uri object based off of it. In my library this is not the case; invalid URI strings are an expected, managed condition, which is handled by a return of NULL and error stack. > >> The advantage of this is: >> >> 1) The relationship between the parser and the validated URI object is >> explicit. >> > > Which is something you don't need to expose, and makes things > complicated. This means I can't do something like: > > uri root("http://www.boost.org/"); > > If users absolutely want this, consider a dual approach: class uri { public: uri(const std::string& uri_str) : uri_impl(parse<std::string, URI<> >(errors, uri_str)) { ... if(!uri_impl) throw(...); scheme = uri_impl->getScheme()) //check for support and set appropriate var's ... } ... }; This is just a different solution -based on my own needs- which I hoped this project would be benefited by. Its also nice to get some peer review on it after several months of hermitage :). I like tags and traits, and feel they could be incorporated into this as well where applicable (Infact, I do plan to refactor this once I have a better grasp on their use). John |