Skip to content

Repository files navigation

Introduction

xnode is a lightweight, header-only C++ library for dynamically typed values with automatic conversion between types.

CI Build Status

License

BSD

Project home

https://github.com/vpiotr/xnode

Purpose

Bringing the power and flexibility of dynamic typing to modern C++, allowing you to write more expressive and adaptable code.

Key Features

  • Zero-cost Abstractions: Header-only with no external dependencies beyond STL
  • Seamless Type Flexibility: Change types at runtime with automatic conversion
  • Modern C++ Design: Template-based, STL-compatible architecture
  • Rich Data Structures: Property lists and vector containers with insertion-order preservation
  • Extensible Type System: Easily add support for your custom types
  • Developer-Friendly API: Intuitive interface with fluent builder methods

Why xnode?

  • Simplify Dynamic Data Handling

     xnode value; value.set_as(5); // Store as integer
    value.set_as(false); // Change to boolean
    value.set_as("dynamic typing"); // Change to string
    // Type safety when you need it
    Assert(value.is<std::string>());
    
  • Smart Type Conversions - Automatic, transparent, and safe

     xnode value;
    value.set_as("123"); // Store as string
    // Automatic conversion when needed
    int num = value.get_as<int>(); // Converts to 123
    // Type safety built-in
    Assert(value.is<std::string>());
    Assert(value.is_convertable_to<int>());
    
  • Flexible Container Types - Including dynamic arrays with tuple-like functionality

     // Modern C++17 version with direct values
    #if __cplusplus >= 201703L
    xarray modern = xarray::of(42, "Direct values", 3.14, true);
    Assert(modern[0].get_as<int>() == 42);
    #endif
    // Create a heterogeneous collection (like a tuple)
    xarray values = xarray::of(
    xnode::value_of(42), // int
    xnode::value_of("Hello World"), // string
    xnode::value_of(3.14), // double
    xnode::value_of(true) // bool
    );
    // Access elements type-safely
    int first = values[0].get_as<int>(); // 42
    std::string second = values[1].get_as<std::string>(); // "Hello World" 
  • Elegant Named Parameters - Cleaner than builder pattern, more flexible than structs

     std::string printInFont(const xnode &font, const std::string &text) {
    const xobject &options = font.get_ref<xobject>();
    // Default values handled elegantly with get_def
    std::ostringstream out;
    out << "text in font [";
    out << "color:" << options.get_def("color", xnode::value_of(0x00ff00)).get_as<int>();
    out << ", font_name:" << options.get_def("font_name", xnode::value_of("courier")).get_as<std::string>();
    out << ", size:" << options.get_def("size", xnode::value_of(10)).get_as<int>();
    out << ", bold:" << options.get_def("bold", xnode::value_of(false)).get_as<bool>();
    out << "] = " << text;
    return out.str(); }
    // Create options with fluent static 'of' method:
    auto result = printInFont(
    xnode::value_of(xobject::of(
    "color", xnode::value_of(0xff0000),
    "font_name", xnode::value_of(std::string("arial")),
    "size", xnode::value_of(12),
    "bold", xnode::value_of(true)
    )), "Hello World!"
    );
    
  • Smart Object Ownership - Automatic memory management for dynamic objects

     // Let xnode handle object lifecycle
    void useObjectOwnership() {
    struct CustomObject {
    int data1;
    int data2;
    };
    // Create an object to be owned by xnode
    std::unique_ptr<CustomObject> obj(new CustomObject{42, 100});
    // Transfer ownership to xnode
    xnode container;
    container.hold(obj.release());
    // Use the object safely
    CustomObject* ptr = container.get_ptr<CustomObject>();
    std::cout << "Values: " << ptr->data1 << ", " << ptr->data2 << std::endl;
    // Object is automatically deleted when container goes out of scope
    }
    
  • Flexible Memory Models - Store owned objects or reference existing memory

     // Work with existing memory without ownership concerns
    void workWithExistingData() {
    // External data we want to reference
    char existingData[] = "Hello xnode!";
    // Store a pointer without taking ownership
    xnode reference;
    reference.set_as(existingData);
    // Use the data through xnode
    char* data = reference.get_as<char*>();
    std::cout << data << std::endl; // Prints "Hello xnode!"
    // xnode won't attempt to free this memory
    }
    

Type Conversion System

xnode comes with a rich set of built-in conversions for seamless interoperability:

  • Fundamental Types: All C++ numeric types (integer and floating-point)
  • Logical Values: bool with smart conversion from/to other types
  • Text Support: Full std::string integration
  • Custom Types: Easily extend with your own conversion rules

Create your own conversion paths by implementing a simple policy class (see xnode_long_double.h for a practical example).

Compatibility

  • Standards: C++11 and later
  • Platforms: Cross-platform (Windows, Linux, macOS)
  • Compilers:
    • Modern GCC/Clang toolchains
    • Microsoft Visual Studio 2015+
    • Any compiler with good C++11 support

Advantages Over Alternatives

  • Lighter than Boost: Self-contained with no external dependencies
  • More Dynamic than std::any: Built-in type conversion
  • More Flexible than std::variant: Change types at runtime
  • More Modern than void*: Type-safe with no casting required

Building

xnode is a header-only library, so you can just include the headers in your project.

Using CMake (Recommended)

xnode uses modern CMake for its build system. To build and install:

# Create build directory
mkdir build
cd build
# Configure (customize your options as needed)
cmake ..
# Build the tests
cmake --build .# Run the tests
ctest
# Install the library (may require elevated privileges)
cmake --install .

CMake Options

  • XNODE_BUILD_TESTS - Build tests (ON by default)

Using the library in your CMake project

After installing, you can use the library in your own CMake project:

find_package(xnodeREQUIRED)
target_link_libraries(your_targetPRIVATExnode::xnode)

Manual Integration

For simple projects, you can just copy the headers from the include directory into your project.

Contact information

Documentation

To build documentation, use Code::Blocks 13 and command:

DoxyBlocks \ Extract documentation

Output will be generated into: "./help/html/" directory in html format.

Release History

See the CHANGELOG.

About

C++ variant data type with on-the-fly conversion

Topics

Resources

Contributing

Stars

12 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages