| Home | Libraries | People | FAQ | More

## Boost.Convert with Standard Algorithms

The following code demonstrates a failed attempt (and one of the reasons Boost.Convert has been developed) to convert a few `string`s to `int`s with `boost::lexical_cast`:

    std::array<char const*, 3> strs = {{ " 5", "0XF", "not an int" }};
    std::vector<int>           ints;

    try
    {
        std::transform(strs.begin(), strs.end(), std::back_inserter(ints),
            std::bind(boost::lexical_cast<int, string>, std::placeholders::_1));

        BOOST_TEST(0 && "Never reached!");
    }
    catch (std::exception&)
    {
        BOOST_TEST(ints.size() == 0); // No strings converted.
    }

If the exception-throwing behavior is the desired behavior, then Boost.Convert supports that. In addition, it also supports a non-throwing process-flow:

    std::array<char const*, 3> strs = {{ " 5", "0XF", "not an int" }};
    std::vector<int>           ints;

    std::transform(strs.begin(), strs.end(), std::back_inserter(ints),
        boost::cnv::apply<int>(boost::cnv::lexical_cast()).value_or(-1));

    BOOST_TEST(ints.size() == 3);
    BOOST_TEST(ints[0] == -1); // Failed conversion does not throw.
    BOOST_TEST(ints[1] == -1); // Failed conversion does not throw.
    BOOST_TEST(ints[2] == -1); // Failed conversion does not throw.

Deploying `boost::cnv::cstream` with better formatting capabilities yields better results with exception-throwing and non-throwing process-flows still supported:

    std::array<char const*, 3> strs = {{ " 5", "0XF", "not an int" }};
    std::vector<int>           ints;
    boost::cnv::cstream         cnv;

    try
    {
        std::transform(strs.begin(), strs.end(), std::back_inserter(ints),
            boost::cnv::apply<int>(std::cref(cnv(std::hex)(std::skipws))));

        BOOST_TEST(0 && "Never reached!");
    }
    catch (boost::bad_optional_access const&)
    {
        BOOST_TEST(ints.size() == 2); // Only the first two strings converted.
        BOOST_TEST(ints[0] ==  5);    // " 5"
        BOOST_TEST(ints[1] == 15);    // "0XF"

        // "not an int" causes the exception thrown.
    }

    std::transform(strs.begin(), strs.end(), std::back_inserter(ints),
        boost::cnv::apply<int>(std::cref(cnv(std::hex)(std::skipws))).value_or(-1));

    BOOST_TEST(ints.size() == 3);
    BOOST_TEST(ints[0] ==  5);
    BOOST_TEST(ints[1] == 15);
    BOOST_TEST(ints[2] == -1); // Failed conversion

| Important
One notable difference in the deployment of `boost::cnv::cstream` with algorithms is the use of `std::cref`. It needs to be remembered that with standard algorithms the deployed converter needs to be copyable or movable (C++11) and is, in fact, copied or moved by the respective algorithm before being used. Given that `std::cstringstream` is not copyable, `boost::cnv::cstream` is not copyable either. That limitation is routinely worked-around using `std::ref` or `std::cref`.

And now an example of algorithm-based integer-to-string formatted conversion with `std::hex`, `std::uppercase` and `std::showbase` formatting applied:

    std::array<int, 3>       ints = {{ 15, 16, 17 }};
    std::vector<std::string> strs;
    boost::cnv::cstream       cnv;

    cnv(std::hex)(std::uppercase)(std::showbase);

    std::transform(ints.begin(), ints.end(), std::back_inserter(strs),
        boost::cnv::apply<string>(std::cref(cnv)));

    BOOST_TEST(strs.size() == 3);
    BOOST_TEST(strs[0] ==  "0XF"); // 15
    BOOST_TEST(strs[1] == "0X10"); // 16
    BOOST_TEST(strs[2] == "0X11"); // 17

#### Forced TypeIn vs. Deduced

So far it was sufficient to explicitly specify only one type to `boost::cnv::apply<TypeOut>` -- the target TypeOut type. The source TypeIn type was provided implicitly through the algorithm and often it all just works (as the examples above demonstrate). However, at times more control is needed regarding the TypeIn type and `boost::cnv::apply()` provides such control via explicit specification of TypeIn -- `boost::cnv::apply<TypeOut, TypeIn>`.

The following example demonstrates an interesting issue related to the `change` class introduced in Integration of User-Defined Types. The class is essentially a glorified `enum`, a user-friendly convenience wrapper around the actual `enum value_type { no, up, dn }`. In the example an array of `change` values (`chgs1`) is sensibly converted to readable "no", "up" and "dn" strings (`strs1`) when an array of `change::value_type` values (`chgs2`) converts to obscure "0", "1" and "2" (`strs2`).

    std::array<change, 3>             chgs1 = {{ change::no, change::up, change::dn }};
    std::array<change::value_type, 3> chgs2 = {{ change::no, change::up, change::dn }};

    auto strs1 = std::vector<std::string>();
    auto strs2 = std::vector<std::string>();
    auto strs3 = std::vector<std::string>();
    auto   cnv = boost::cnv::cstream();

    std::transform(chgs1.begin(), chgs1.end(), std::back_inserter(strs1),
        boost::cnv::apply<string>(std::cref(cnv))); // Deduced TypeIn is 'change'

    std::transform(chgs2.begin(), chgs2.end(), std::back_inserter(strs2),
        boost::cnv::apply<string>(std::cref(cnv))); // Deduced TypeIn is 'change::value_type'

    BOOST_TEST(strs1.size() == 3);
    BOOST_TEST(strs1[0] == "no");
    BOOST_TEST(strs1[1] == "up");
    BOOST_TEST(strs1[2] == "dn");

    BOOST_TEST(strs2.size() == 3);
    BOOST_TEST(strs2[0] == "0");
    BOOST_TEST(strs2[1] == "1");
    BOOST_TEST(strs2[2] == "2");

The `boost::cnv::apply<TypeOut, TypeIn>` with forced (rather than deduced) TypeIn comes to the rescue and converts the array of `change::value_type` values (`chgs2`) to sensible "no", "up" and "dn" (`strs3`):

    std::transform(chgs2.begin(), chgs2.end(), std::back_inserter(strs3),
        boost::cnv::apply<string, change>(std::cref(cnv)));

    BOOST_TEST(strs3.size() == 3);
    BOOST_TEST(strs3[0] == "no");
    BOOST_TEST(strs3[1] == "up");
    BOOST_TEST(strs3[2] == "dn");

| Note
For demonstration purposes the example above is made as simple as possible and, consequently, the described "issue" could probably be addressed by other means. Still, do not let my inability to come up with a better (complex but short and representative) example get in the way of appreciating the described functionality. I do not expect it to be used often but it is here when you need it.