The Artima Developer Community
Sponsored Link

Prescriptions, Proscriptions, and Prognostications
An exercise in compromise in class interface refinement.
by Matthew Wilson
June 1, 2007
Summary
Attempting to find a compromise between the constraints of a facade that wraps a system API, the limitations of a limited namespace naming scheme, and a user wanting more expressiveness, revealed an interesting compromise in design.

Advertisement

A recent post from an STLSoft user expressing dissatisfaction with the STLSoft Windows Registry Library highlighted an interesting conundrum. The user wished to create a registry key in a single statement, something like:

  winstl::reg_key  key(HKEY_CURRENT_USER, "SOFTWARE\\MyCompany\\MyApp");
This uses the winstl::reg_key class, which is actually a typedef to a specialisation of winstl::basic_reg_key. (Like std::basic_string, winstl::basic_reg_key can support different character encodings. Thus, winstl::reg_key is a typedef of winstl::basic_reg_key<TCHAR>. See here for more details.)

The problem with the above statement, from the perspective of our user, is that it does not create a key. It only opens the key SOFTWARE\MyCompany\MyApp if it already exists. If it does exist, then an instance of winstl::registry_exception is thrown, carrying the result code (ERROR_FILE_NOT_FOUND) returned by the underlying Registry API call, RegOpenKeyEx().

The corresponding constructor for winstl::basic_reg_key has three parameters, as follows:
  // In namespace winstl
  template <. . .>
  class basic_reg_key
  {
    . . .
  public: // Construction
    basic_reg_key(HKEY keyParent, char_type const* subkeyName, REGSAM accessMask = KEY_ALL_ACCESS);
    . . .
  public: // Sub-key operations
    basic_reg_key create_sub_key(char_type const* subKeyName, REGSAM accessMask = KEY_ALL_ACCESS);
REGSAM is a Windows API typedef from ACCESS_MASK, itself a typedef from DWORD. KEY_ALL_ACCESS is defined as follows:
  #define KEY_ALL_ACCESS ((STANDARD_RIGHTS_ALL | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) & (~SYNCHRONIZE))
In other words, it's a whole lot of flags covering the bit-width of a DWORD. The consequence of this is that the third constructor parameter, carrying all possible (now and future) values that may be passed to RegOpenKeyEx(), cannot also carry an additional flag to enable creation by constructor, as in:
  winstl::reg_key  key(HKEY_CURRENT_USER, "SOFTWARE\\MyCompany\\MyApp", KEY_ALL_ACCESS | winstl::reg_key::createKey);
The reason is simple. Whatever value we might select for a member constant/enumerator winstl::reg_key::createKey cannot be guaranteed not to clash with Windows API constants to be used with the Windows Registry API functions. (If you're interested, I talk about this issue at more length in Chapter 17 of my new book, Extended STL, volume 1: Collections and Iterators, which is published in three weeks' time. Coincidentally.)

Given this restriction, we must look for an alternative. Our user (rightly) observed that the following single line form, which does have the desired semantics, is syntactically unappealing:
  winstl::reg_key  key = winstl::reg_key(HKEY_CURRENT_USER, "SOFTWARE").create_sub_key("MyCompany\\MyApp");
and instead suggested adding a function to support syntax of the form:
  winstl::reg_key  key = winstl::create_reg_key(HKEY_CURRENT_USER, "SOFTWARE\\MyCompany\\MyApp");
The problem here is that, for good or ill, the use of namespaces within the STLSoft libraries is restricted to a minimum. Specifically, elements from each of the sub-projects (COMSTL for COM components: namespace comstl; UNIXSTL for UNIX components: unixstl; WinSTL for Windows components: winstl; and so on) are defined within their single, top-level namespace. (If you're interested, this is an artefact of the early history of STLSoft, wherein compilers that did not (properly) support namespaces were supported by the libraries. If there is ever an STLSoft 2.x - we're currently at 1.9.1 - I might seek to address this and employ library-specific namespaces. But for now it is what it is.)

Hence, as shown above, a function inserted into the winstl namespace to create a key could not simply be named create(), or even create_key(), because that is not specific to the registry. So, it'd have to be reg_create_key() or, as the user suggested create_reg_key(). This option, with either name, does not appeal to me, as the function will be ligging around the winstl namespace with other free functions. But it's not really a free function, is it? It's actually a non-member function that is part of the winstl::basic_reg_key interface.

The answer to this conundrum is to declare a static creator method, as in:
  // In namespace winstl
  template <. . .>
  class basic_reg_key
  {
    . . .
  public: // Construction
    basic_reg_key(HKEY keyParent, char_type const* subkeyName, REGSAM accessMask = KEY_ALL_ACCESS);
    static basic_reg_key create_key(HKEY keyParent, char_type const* subkeyName, REGSAM accessMask = KEY_ALL_ACCESS);
    . . .
  public: // Sub-key operations
    basic_reg_key create_sub_key(char_type const* subKeyName, REGSAM accessMask = KEY_ALL_ACCESS);
This affords the following, relatively expressive, syntax:
  winstl::reg_key  key = winstl::reg_key::create_key(HKEY_CURRENT_USER, "SOFTWARE\\MyCompany\\MyApp");
To be sure, it's not as expressive as the straight (opening, not creating) constructor call. But that's actually a good thing, because it is actually doing something different and significant: creating a key rather than opening it. Positively, it remains closely associated with the STLSoft Windows Registry Library despite there not being a distinct namespace for it.

For your interest, the implementation of the method is very simple:
  template <. . .>
  basic_reg_key<. . .> basic_reg_key<. . .>::create_key(HKEY keyParent, char_type const* subkeyName, REGSAM accessMask)
  {
    return basic_reg_key(hkey, NULL, KEY_CREATE_SUB_KEY).create_sub_key(subKeyName, accessMask);
  }
The only noteworthy point is that the accessMask is used only for the call to create_sub_key(), whereas the constructor permission is KEY_CREATE_SUB_KEY. no more, no less.

The updated basic_reg_key class will be included in STLSoft 1.9.2, to be released in June.

Talk Back!

Have an opinion? Readers have already posted 4 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Matthew Wilson adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Matthew Wilson is a software development consultant and creator of the FastFormat, Pantheios and STLSoft libraries. He is author of the books Imperfect C++ (Addison-Wesley, October 2004) and Extended STL, volume 1 (Addison-Wesley, 2007), and is currently working on his third, Breaking Up The Monolith: Advanced C++ Design Without Compromise. He has published over 60 articles on C++ and other topics, and has served as columnist and contributing editor for C/C++ Users Journal. Matthew believes that code should be discoverable and largely self-documenting, and lives up to that by being a hopeless documentor. He can be contacted via http://www.imperfectcplusplus.com/ or stlsoft@gmail.com.

This weblog entry is Copyright © 2007 Matthew Wilson. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use