// Copyright (c) 2009-2020 Vladimir Batov.
// Use, modification and distribution are subject to the Boost Software License,
// Version 1.0. See http://www.boost.org/LICENSE_1_0.txt.

#include <boost/convert.hpp>
#include <boost/convert/stream.hpp>
#include <boost/convert/lexical_cast.hpp>
#include <boost/detail/lightweight_test.hpp>
#include <array>
#include <vector>
#include <functional>

using std::string;

static
void
introduction()
{
//[algorithm_introduction

   /*`The following code demonstrates conversion of an array of integers from their textual ['hexadecimal]
      representation. It assigns -1 to those which fail to convert:
   */

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

    // Configure converter to read hexadecimal, skip (leading) white spaces.
    cnv(std::hex)(std::skipws);

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

    BOOST_TEST(ints.size() == 3); // Number of values processed.
    BOOST_TEST(ints[0] ==  5);    // " 5"
    BOOST_TEST(ints[1] == 15);    // "0XF"
    BOOST_TEST(ints[2] == -1);    // "not an int"
//]
}

static
void
example1()
{
//[algorithm_example1
    /*`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.
    }
//]
}

static
void
example2()
{
//[algorithm_example2
    /*`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.
//]
}

static
void
example3()
{
//[algorithm_example3
    /*`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.
    }
//]
}

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

//[algorithm_example4

    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
       [@http://en.cppreference.com/w/cpp/named_req/TriviallyCopyable copyable] or
       [@http://en.cppreference.com/w/cpp/named_req/MoveAssignable 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`.]
    */
//]
}

static
void
example5()
{
//[algorithm_example5
    /*`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
//]
}

int
main(int, char const* [])
{
    introduction();
    example1();
    example2();
    example3();
    example4();
    example5();

    return boost::report_errors();
}
