Visual C++ in Short: Converting between numbers and strings

C++ developers have a number of options available to them for converting between numbers and strings, few of which are very appealing. Most developers are familiar with the likes of atoi and itoa from the C Run-Time Library. The main problem is that these functions don’t have a coherent way of reporting errors. Although there have been some attempts to improve these functions, most notably the addition of the security enhancements introduced by Microsoft, they are still not very helpful.

Fortunately the Windows SDK includes a very comprehensive collection of functions that it inherited from OLE Automation to convert between variant-compatible types. These functions don’t actually require you to use variants or even initialize OLE or COM. They merely provide the functionality for converting between many of the types that you could store in a variant.

The functions are declared in OleAuto.h and take the form VarXxxFromYyy where Xxx is the desired type and Yyy is the original type of the data. You can use “Var[^ ]+From[^ (]+” to search for them from within Visual Studio.

 

The functions for converting strings to numbers are prototyped as follows:

HRESULT VarXxxFromStr(const WCHAR* string, LCID localeId, ULONG flags, T* number);

The source is a pointer to a Unicode string (and does not have to be a BSTR). The locale identifier affects how the string is interpreted. You can for example use the user’s regional settings as defined by the current thread’s locale identifier using the GetThreadLocale function. Alternatively you can specify LOCALE_INVARIANT if you need the conversions to be independent of the user’s locale. This might be the case if you’re using the text form of a number not to display to the user but rather to persist in some format like XML. The flags are mostly for controlling date and time conversions and should be zero in most other cases.

The following example shows how to convert a string to a double in a locale-independent manner:

double value = 0;

const HRESULT result = ::VarR8FromStr(L"1234.567",
                                      LOCALE_INVARIANT,
                                      0, // flags
                                      &value);

if (FAILED(result))
{
    // The HRESULT describes why the conversion failed,
    // e.g. DISP_E_TYPEMISMATCH, DISP_E_OVERFLOW
}

ASSERT(1234.567 == value);

The functions for converting numbers to strings are prototyped as follows:

HRESULT VarBstrFromYyy(T number, LCID localeId, ULONG flags, BSTR* string);

These functions all return the strings as BSTR values so the caller is responsible for freeing them using the SysFreeString function. A better approach is to use ATL’s CComBSTR wrapper class.

The following example shows how to convert a double to a string using the locale associated with the current thread and then converts the string back to a double again:

#include <atlbase.h>

#define ASSERT ATLASSERT

int main()
{
    double double1 = 1234.567;

    CComBSTR string;

    HRESULT result = ::VarBstrFromR8(double1,
                                     ::GetThreadLocale(),
                                     0, // flags
                                     &string);

    if (FAILED(result))
    {
        // The HRESULT describes why the conversion failed,
        // e.g. E_OUTOFMEMORY
    }

    double double2 = 0;

    result = ::VarR8FromStr(string,
                            ::GetThreadLocale(),
                            0, // flags
                            &double2);

    if (FAILED(result))
    {
        // The HRESULT describes why the conversion failed,
        // e.g. DISP_E_TYPEMISMATCH, DISP_E_OVERFLOW
    }

    ASSERT(double1 == double2);
}

Keep in mind that the thread locale can change at any time so you must not persist a localized version of a number as you may not be able to parse it later on. It is however useful for displaying and accepting input from the user.

If you’re looking for one of my previous articles here is a complete list of them for you to browse through.

Produce the highest quality screenshots with the least amount of effort! Use Window Clippings.

4 Comments

  • Nice. I'm liking the C++ in short format! Thanks.

    John

  • I fail to see why you wrote it. std C++ already provides basic_istringstream and basic_ostringstream classes which are exactly for that purpose. They even allow proper UDT conversions too.

    In the last post, you were in reverence with ATL's regular expression support while TR1 and boost provide a conformant implementation.

  • Tanveer Badar: I think I’m going to have to address the STL vs. ATL issue more formally. As I mentioned in the comments on the post about regular expressions, I think the STL and TR1 additions are excellent. I do however prefer to use libraries that don’t require me to add exception handling. Anyway, hopefully I’ll be able to explain my position a bit clearer in an upcoming blog post.

  • I'll eagerly wait for your take on exception handling in that case.

Comments have been disabled for this content.