# [`cxx-str`][]

A [C++11][] [`std::string`][] [macro][] library.

If [C++20][] is avaiable, [`std::format`][] and [`std::source_location`][] may
be more appropriate. If [C++23][] is available, [`std::stacktrace`][] may be
more appropriate.

[`cxx-str`]: https://git.rcrnstn.net/rcrnstn/cxx-str
[C++11]: https://en.wikipedia.org/wiki/C++11
[`std::string`]: https://en.cppreference.com/w/cpp/string/basic_string
[macro]: https://en.cppreference.com/w/cpp/preprocessor/replace
[C++20]: https://en.wikipedia.org/wiki/C++20
[`std::format`]: https://en.cppreference.com/w/cpp/utility/format/format
[`std::source_location`]: https://en.cppreference.com/w/cpp/utility/source_location
[C++23]: https://en.wikipedia.org/wiki/C++23
[`std::stacktrace`]: https://en.cppreference.com/w/cpp/utility/basic_stacktrace

## Usage

Note that macros can result in unintuitive behavior.

Looking at the [source][] and [test][] is the best way to understand usage.

Throughout, arguments are only evaluated once. When [lambda][]s are defined and
immediately called behind the scenes, values are implicitly [capture][]d by
reference. The only lambda that implicitly captures by value is
`STR_FUNC(VALUE)`.

Include the header:

```cpp
#include <str.hpp>
```

There are a few different categories of functionality:

-   Easy access to [`std::ostringstream`][]'s [`operator<<`][] and [`str()`][].
    The `VALUE` expressions below can contain several values joined with `<<`.

    `STR(VALUE)` produces a string.

    `STR_FUNC(VALUE)` produces a [lambda][], which implicitly [capture][]s by
    value, and returns a string. Useful for lazy evaluation of costly string
    operations. For good performance, make sure the standard library can
    perform Small String Optimization ([SSO][]) (and if used with
    [`std::function`][], Small Object Optimization ([SOO][])).

    `STR_JOIN(JOIN, ITERATOR, VALUE, ...)` makes the variable name `ITERATOR`
    available in the `VALUE` expression, and joins the corresponding non-empty
    (as interpreted by `STR(VALUE)`) elements of `...` with `JOIN`.

    `STR_JOIN_INIT(JOIN, ITERATOR, VALUE, TYPE, ...)` forwards to
    `STR_JOIN(JOIN, ITERATOR, VALUE, ...)`, passing
    [`std::initializer_list`][]`<TYPE>`[`__VA_ARGS__`][]`)` as the last
    argument. Notice the lack of braces, they have to be supplied manually.

    `STR_JOIN_INITSTR(JOIN, ITERATOR, VALUE, ...)` forwards to `STR_JOIN_INIT`
    with `std::string` as the `TYPE` argument.

-   Easy access to the stringifying and concatenating [`#` and `##`
    operators][] to help map constants to strings.

    `STR_CASE(CASE)` uses `case` and `return` statements: `case (CASE): return
    (#CASE);`.

    `STR_COND(VALUE, CASE)` uses the [conditional operator][]: `((VALUE) ==
    (CASE)) ? (#CASE) :`.

    `STR_INIT(CASE)` uses [list initialization][]: `{(CASE), (#CASE)},`.

    `STR_ARGS(ARG)` expands to `ARG, #ARG`.

    `STR_ARGS_PREFIX(PREFIX, ARG)` expands to `PREFIX##ARG, #ARG`.

-   Easy access to source file and line.

    `STR_HERE(VALUE)` joins [`__FILE__` and `__LINE__`][] and [`__func__`][]
    with `":"`, and appends `": "` and the given argument to produce a string.

-   Easy handling to ([nested][]) [`std::exception`][]s, which contain strings
    accessible through the [`what()`][] method.

    `STR_EXCEPTION` can be defined to control what exceptions are thrown by the
    macros below. If it is not defined, [`std::runtime_error`][] is used.

    `STR_THROW(VALUE)` [`throw`][]s a `STR_EXCEPTION` initialized with
    `STR(VALUE)`.

    `STR_THROW_ERRNO()` calls `STR_THROW` with the argument
    `std::generic_category().message(errno) << "."`.

    `STR_RETHROW(VALUE)` calls [`std::terminate`][] if called outside a
    [`catch`][] block. Otherwise, `STR_THROW`s `VALUE` followed by the
    [`what()`][] of the current exception (so it is appropriate to include a
    separator manually) if it is a (derives from) [`std::exception`][],
    otherwise simply rethrows the current exception.

    `STR_NESTED_THROW(VALUE)` calls [`std::throw_with_nested`][] on a
    `STR_EXCEPTION` initialized with `STR(VALUE)`.

    `STR_NESTED_THROW_ERRNO()` calls `STR_NESTED_THROW` with the argument
    `std::generic_category().message(errno) << "."`.

    `STR_NESTED_WHAT(JOIN, EXCEPTION)` joins the strings returned by the
    (optionally) [nested][] [`std::exception`][]-based `EXCEPTION`'s
    [`what()`][] methods with `JOIN`, and returns the resulting string.

[source]: include/str.hpp
[test]: tests/str.cpp
[lambda]: https://en.cppreference.com/w/cpp/language/lambda
[capture]: https://en.cppreference.com/w/cpp/language/lambda#Lambda_capture
[`std::ostringstream`]: https://en.cppreference.com/w/cpp/io/basic_ostringstream
[`operator<<`]: https://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt
[`str()`]: https://en.cppreference.com/w/cpp/io/basic_ostringstream/str
[SSO]: https://en.cppreference.com/w/cpp/language/acronyms
[`std::function`]: https://en.cppreference.com/w/cpp/utility/functional/function
[SOO]: https://en.cppreference.com/w/cpp/language/acronyms
[`std::initializer_list`]: https://en.cppreference.com/w/cpp/utility/initializer_list
[`__VA_ARGS__`]: https://en.cppreference.com/w/cpp/preprocessor/replace
[`#` and `##` operators]: https://en.cppreference.com/w/cpp/preprocessor/replace#.23_and_.23.23_operators
[list initialization]: https://en.cppreference.com/w/cpp/language/list_initialization
[conditional operator]: https://en.cppreference.com/w/cpp/language/operator_other#Conditional_operator
[`__FILE__` and `__LINE__`]: https://en.cppreference.com/w/c/preprocessor/line
[`__func__`]: https://en.cppreference.com/w/cpp/language/function#func
[`#ifndef`]: https://en.cppreference.com/w/cpp/preprocessor/conditional
[`NDEBUG`]: https://en.cppreference.com/w/c/error/assert
[nested]: https://en.cppreference.com/w/cpp/error/nested_exception
[`std::exception`]: https://en.cppreference.com/w/cpp/error/exception
[`what()`]: https://en.cppreference.com/w/cpp/error/exception/what
[`std::runtime_error`]: https://en.cppreference.com/w/cpp/error/runtime_error
[`throw`]: https://en.cppreference.com/w/cpp/language/throw
[`std::terminate`]: https://en.cppreference.com/w/cpp/error/terminate
[`catch`]: https://en.cppreference.com/w/cpp/language/try_catch
[`std::throw_with_nested`]: https://en.cppreference.com/w/cpp/error/throw_with_nested

## Test output

The [test][] outputs:

```
0x3
0x3
"first", "", "third"
first, third

first
unknown
third

/path/to/cxx-str/tests/str.cpp:96: Hello from here

Failed to do outer:
Failed to do inner:
Expected this, got that.
```

## Building

See [`BUILDING.md`][].

[`BUILDING.md`]: BUILDING.md

## License

Licensed under the [ISC License][] unless otherwise noted, see the
[`LICENSE`][] file.

[ISC License]: https://choosealicense.com/licenses/isc/
[`LICENSE`]: LICENSE
