August 2004 - Posts

Explicit Constructors

A reviewer recently noticed the use of the explicit keyword in some of my sample code and wondered what it was for.

The explicit keyword in C++ is used to declare explicit constructors. Explicit constructors are simply constructors that cannot take part in an implicit conversion. Consider the following example:

class Array
{
public:
    Array(size_t count);
    // etc.
};

This seems innocent enough until you realize that you can do this:

Array array = 123;

Well that’s ugly but hardly something to get excited about, or is it? This is known as an implicit conversion. The trouble is that it can occur in more insidious places. Consider the following function:

void UseArray(const Array& array);

Because the compiler will attempt to find an implicit conversion, the following code compiles just find:

UseArray(123);

Yikes! That’s terrible. First there is the problem of code clarity. What does this mean? Secondly, because the implicit conversion involves calling the Array constructor, presumably enough storage is being allocated for a new Array object, not to mention the memory reserved for 123 elements contained in the array. Surely this is not what the programmer intended!?

All of these subtleties can be avoided by making the Array constructor explicit:

class Array
{
public:
    explicit Array(size_t size);
    // etc.
};

Now a programmer needs to be explicit about her use of the Array constructor.

Array array(123);
UseArray(Array(123));

A thing of beauty.

So why aren’t all constructors explicit? Well in a small number of cases it makes sense to allow an implicit conversion to allow user-defined types to behave like built-in types. A canonical example would probably be a high-precision numeric type:

Number number = 123;

In this case it makes sense to provide a more natural expression.

And finally, if you have two or more required constructor arguments then it does not make sense to make the constructor explicit since it cannot be used in implicit conversions anyway:

class UserName
{
public:
    UserName(const std::wstring& principal,
             const std::wstring& authority);
};

Making the UserName constructor explicit would have no effect. You might be tempted to give authority a default value and then allow UserName to take part in an implicit conversion. When considering this, you should always think about whether it could introduce an error by default. Consider what happens if we do this.

class UserName
{
public:
    UserName(const std::wstring& principal,
             const std::wstring& authority = std::wstring());
    // etc.
};

Now the programmer can write the following dubious code:

UserName userName = L”kenny@kennyandkarin.com”;

What’s wrong with that? Well the entire string is passed as the principal and the programmer’s entirely innocent code is now incorrect. A better solution is to return to the original UserName class design and then the use of the UserName class is clear:

UserName username(“kenny”,
                  “kennyandkarin.com”);

Since both arguments are required, it is much harder for the programmer to neglect the one, and setting the authority to an empty string would be easier to spot as an error in a code review. You might even have the UserName constructor throw an ArgumentException if the authority is empty.


© 2004 Kenny Kerr

 

Posted by KennyKerr with 2 comment(s)

IIS Web Site Port Numbers

Thanks for all the interesting comments on my previous post. I plan to talk more about the subject of code quality and pragmatism in the future.

A developer recently asked me how she could programmatically determine the port number for a particular IIS web site running on Windows Server 2003. One of the nice things about IIS 6 is that it stores the metabase as an XML document under the system directory. This provides a much simpler mechanism (in my opinion) for querying and configuring IIS compared to what was available in older versions, as long as you’re familiar with the schema. The code snippet below prints out the web sites in IIS and their respective port numbers.

 

// Get the path to the metabase document.

String^ path = Environment::GetFolderPath(Environment::SpecialFolder::System);

path = IO::Path::Combine(path,
                         "inetsrv\\metabase.xml");

// Create an XPathNavigator to search the document.

Xml::XPath::XPathDocument^ document = gcnew Xml::XPath::XPathDocument(path);
Xml::XPath::XPathNavigator^ navigator = document->CreateNavigator();
navigator->MoveToChild(Xml::XPath::XPathNodeType::Element);

// Determine the namespace for the document and associate it with a prefix
// to use in expressions.

Xml::XmlNamespaceManager^ resolver = gcnew Xml::XmlNamespaceManager(document->NameTable);
String^ defaultNamespace = navigator->LookupNamespace(String::Empty);

resolver->AddNamespace("iis",
                       defaultNamespace);

// Iterate over all IIsWebServer elements with a ServerBindings attribute.

Xml::XPath::XPathNodeIterator^ iter = navigator->Select("//iis:IIsWebServer[@ServerBindings]",
                                                        resolver);

// The regular expression allows us to easily extract the port number from
// the ip:port:host tuple.

Text::RegularExpressions::Regex^ port = gcnew Text::RegularExpressions::Regex("^[^:]*:(\\d+):[^:]*$");

while (iter->MoveNext())
{
    Xml::XPath::XPathNavigator^ node = iter->Current;

    // Only print the server details if the expression matches a port number.

    Text::RegularExpressions::Match^ match = port->Match(node->GetAttribute("ServerBindings",
                                                                            String::Empty));

    if (match->Success)
    {
        Console::Write("Description: ");

        Console::WriteLine(node->GetAttribute("ServerComment",
                                              String::Empty));

        Console::Write("Location: ");

        Console::WriteLine(node->GetAttribute("Location",
                                              String::Empty));

        Console::Write("Port: ");
        Console::WriteLine(match->Groups[1]->Value);
        Console::WriteLine();
    }
}

Here is the output I get on one of my servers:

 

Description: Default Web Site
Location: /LM/W3SVC/1
Port: 80

Description: Microsoft SharePoint Administration
Location: /LM/W3SVC/2
Port: 3062

Description: Virtual Server
Location: /LM/W3SVC/3
Port: 1024


© 2004 Kenny Kerr

 

Posted by KennyKerr with no comments

On Code Quality and Pragmatism

My good friend Ari Glaizel has just started blogging. He is one of the most well-rounded software developers I have come across. I’m looking forward to some interesting posts. He does however tend to be quite pragmatic towards his approach to software development. Talking about working on a deadline he writes:

“If there is a bit more hard-coding than I like to have, that's fine. If there are inherent weaknesses in the solution that’s fine too as long as it’s documented in the code and your team is aware and accepts it.”

I tend to take a harder stance on code quality. I firmly believe that it does not take any more time to write top quality code than it does to write “pragmatic”, just-get-it-done code. The other problem I have is that it’s not enough to document your “hacks” in the source code and make sure you’ve told your team lead (unless he rightly kicks your ass) as it will inevitably be forgotten until one of the assumptions you made about the code is broken just as a potential customer tries to use your product for the first time.

One problem is when you have programmers on your team that do not know how to write quality code to begin with. Even if their managers aren’t breathing down their necks to just get it done, they will inevitably write poor quality code. One of the problems is that it takes years of practice and learning to acquire the experience and knowledge of what it means to write solid code. Students in university are apparently taught that after completing their degree that they are qualified to write production code. Books like Code Complete attempt to bring years of industry knowledge of quality coding practices to the average developer, but I am often disappointed at programmers I come across who feel they do not need to read such books.

The trouble is that it is too easy to say “well this solution will have to do for now – I’ll fix it later” because it is too easily forgotten until it comes back to bite you in the ass and your product support guy asks you why it crashes when you click the File | Open menu item with a mouse but if you use a keyboard to select the command it works just fine (that’s a real bug but I forget in which product I saw it). Every line of code you ever write needs to be written with as much thought and foresight as you can muster because that code will come back to haunt you if you are careless. What is that you say? “There’s a one in a million chance that it will break.” Well, one in a million is next Tuesday.


© 2004 Kenny Kerr

Posted by KennyKerr with 6 comment(s)

C++/CLI Language Specification Updated

A new version of the C++/CLI language specification was posted today. You can download it here. After reading my article on C++/CLI, you may want to keep an eye on this document for more information on the language design.

This is an update to the original candidate base document Microsoft contributed back in November and is now owned by ECMA.

Thanks to Brian Johnson for making this available!


© 2004 Kenny Kerr

Posted by KennyKerr with 1 comment(s)
More Posts