Skip to content

Getting Started

Michael Hansen edited this page Apr 30, 2020 · 5 revisions

Getting Started with string_theory

Building string_theory

string_theory uses the CMake build system. To get started on a Unix-like system (Linux, Mac, MSYS, etc), you can just build string_theory from the command line after installing CMake:

$ cd /path/to/string_theory
$ mkdir build && cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
$ make && sudo make install

On Windows, you can also generate Visual Studio projects, either from the command line or from the CMake GUI:

> cd C:\Path\to\string_theory
> mkdir build-msvc
> cd build-msvc
> cmake -G "Visual Studio 14 2015" -DCMAKE_INSTALL_PREFIX="..\dist-msvc" ..

Open the generated .sln file and build the Release target,
 - OR -
Build from the command line:
> cmake --build . --config Release
> cmake --build . --config Release --target INSTALL

Using string_theory in your project

If your project is built using CMake, it should be easy to add string_theory:

# CMakeLists.txt
# ...
find_package(string_theory REQUIRED)
# ...
add_executable(myapp ...)
target_link_libraries(myapp PUBLIC string_theory)

Then be sure to specify or locate the cmake module path when configuring your project (e.g. -Dstring_theory_DIR=/usr/local/lib/cmake/string_theory).

When using other build systems, you can just specify string_theory's include and library paths for your build system. For GNU makefiles, this may look something like:

STRING_THEORY_DIR = /usr/local
CPPFLAGS += -I$(STRING_THEORY_DIR)/include

If you create a useful module for your favorite build system, you can also contribute the necessary files to string_theory via a Pull Request.

Examples

Basic ST::string features

#include <string_theory/string>
#include <string_theory/exceptions>
#include <string_theory/stdio>

int main(int argc, char *argv[])
{
    ST::string greeting = ST_LITERAL("Hello");

    // Literals can also use the _st user-literal operator if your compiler
    // supports user-defined literals
    ST::string name = "world"_st;
    if (argc > 1) {
        try {
            // Alternatively, you can assume UTF-8 and substitute invalid
            // byte sequences with
            // ST::string::from_utf8(argv[1], ST_SIZE_AUTO, ST::substitute_invalid)
            // The above method will also avoid throwing any exceptions.
            name = ST::string::from_utf8(argv[1]);
        } catch (const ST::unicode_error &) {
            name = ST::string::from_latin_1(argv[1]);
        }
    }
    ST::printf("{}, {}\n", greeting, name);

    return 0;
}

String formatting

#include <string_theory/format>

extern void log_line(const ST::string &line);

ST::string status_line(const ST::string &filename, int file_num, int total_files)
{
    // "Processing file 004: Foobar.txt          - 12.5%"
    return ST::format("Processing file {>03}: {<20} - {4.1f}%", file_num,
                      filename, double(file_num) / double(total_files));
}

int main(int argc, char *argv[])
{
    for (int i = 1; i < argc; ++i) {
        log_line(status_line(argv[i]));
        // Process file...
    }
    return 0;
}

Codecs

#include <string_theory/codecs>
#include <string_theory/stdio>

int main(int argc, char *argv)
{
    if (argc < 2) {
        ST::printf(stderr, "Usage: {} <mode> input\n", argv[0]);
        ST::printf(stderr, "<mode>:\n");
        ST::printf(stderr, "  base64_encode\n");
        ST::printf(stderr, "  base64_decode\n");
        ST::printf(stderr, "  hex_encode\n");
        ST::printf(stderr, "  hex_decode\n");
        return 1;
    }

    ST::string mode = argv[1];
    ST::string input = argv[2];
    ST::string result;
    if (mode == "base64_encode") {
        result = ST::base64_encode(input.to_utf8());
    } else if (mode == "base64_decode") {
        ST::char_buffer data = ST::base64_decode(input);
        result = ST::string::from_utf8(data);
    } else if (mode == "hex_encode") {
        result = ST::hex_encode(input.to_utf8());
    } else if (mode == "hex_decode") {
        ST::char_buffer data = ST::hex_decode(input);
        result = ST::string::from_utf8()
    } else {
        ST::printf(stderr, "ERROR: Unsupported mode: {}.\n", mode);
        return 2;
    }
    ST::printf("{}\n", result);

    return 0;
}

Custom formatter

/* point3d.h */
#include <string_theory/formatter>

struct Point3D { double x, y, z; };

// This version will apply the specified floating point formatting rules
// to each of the rendered values x,y,z
inline void format_type(const ST::format_spec &format, ST::format_writer &output,
                        const Point3D &value)
{
    output.append("Point3D{");
    ST::format_type(format, output, value.x);
    output.append(",");
    ST::format_type(format, output, value.y);
    output.append(",");
    ST::format_type(format, output, value.z);
    output.append("}");
}

// Could also format recursively.  This will instead treat the upper-level
// formatting rules as a single string formatter.  For example:
inline void format_type(const ST::format_spec &format, ST::format_writer &output,
                        const Point3D &value)
{
    using namespace ST;   // Use ADL if necessary
    format_type(ST::format("Point3D{{{.2f},{.2f},{.2f}}", value.x, value.y, value.z));
}
#include <string_theory/stdio>
#include "point3d.h"

int main(int argc, char *argv[])
{
    Point3D point{12.4, 42.0, -6.3};

    ST::printf("It's located at {}\n", point);

    return 0;
}

Custom format writer

#include <string_theory/formatter>
#include "my_logger.h"

class my_log_writer : public ST::format_writer
{
public:
    my_log_writer(const char *format_str, my_logger *logger)
        : ST::format_writer(format_str), m_logger(logger) { }

    my_log_writer &append(const char *data, size_t size) override
    {
        // NOTE: Prior to string_theory 3.0, you MUST check for
        // ST_AUTO_SIZE.  In string_theory 3.0 and later, formatters
        // should not call append() with ST_AUTO_SIZE any more.
        // if (size == ST_AUTO_SIZE)
        //     size = std::char_traits<char>::length(data);
        m_logger->write_log(data, size);
        return *this;
    }

    my_log_writer &append_char(char ch, size_t count = 1) override
    {
        ST::string buffer = ST::string::fill(count, ch);
        m_logger->write_log(buffer.c_str(), count);
        return *this;
    }
};

template <typename... args_T>
void log_format(my_logger *logger, const char *fmt_str, args_T &&...args)
{
    my_log_writer data(fmt_str, logger);
    ST::apply_format(data, std::forward<args_T>(args)...);
}

int main(int argc, char *argv[])
{
    my_logger *logger = get_logger();
    log_format(logger, "Running {}\n", argv[0]);

    return 0;
}