Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Tutorial

Accessing POD member by index
Flattening
Flat or Precise functions to choose
Three ways of getting operators
Reflection of unions

The following example shows how to access structure fields by index using boost::pfr::get.

Let's define some structure:

#include <boost/pfr/precise/core.hpp>

struct foo {            // defining structure
    int some_integer;
    char c;
};

We can access fields of that structure by index:

foo f {777, '!'};
auto& r1 = boost::pfr::get<0>(f); // accessing field with index 0, returns reference to `foo::some_integer`
auto& r2 = boost::pfr::get<1>(f); // accessing field with index 1, returns reference to `foo::c`
[Warning] Warning

All the functions and metafunctions in Boost.PFR that start with flat_ represent template parameter type as flat structure without static members!

Take a look at the struct my_struct:

#include <boost/pfr/flat/core.hpp>

struct my_struct_nested { short a1; int a2; };

struct my_struct {
    int a0;
    static const int cvalue = 1000;
    my_struct_nested nested;
    short a3_a4[2];
};

It will be flattened and represented as:

struct my_struct_flat {
    int     a0;
    short   a1;
    int     a2;
    short   a3;
    short   a4;
};

It means, that:

boost::pfr::flat_get<2>(my_struct{});   // ... will return `my_struct::my_struct_nested::a2` field.
boost::pfr::flat_get<3>(my_struct{});   // ... will return `my_struct::a3_a4[0]` field.

Exactly the same story with arrays:

int i[2][2] = {{10, 11}, {12, 13} };
const int& r = boost::pfr::flat_get<1>(i);
assert(r == 11);

All the functions that have flat_ prefix and are declared in boost/pfr/flat/* headers the flat functions, other function are precise and are declared in boost/pfr/precise/*. In previous example you've seen how the the flattening works.

  • If you wish types flattened - use flat functions
  • If you use types with C arrays - use flat functions
  • Otherwise use precise functions
[Warning] Warning

MSVC currently supports only precise functions and only in /std:c++latest or /std:c++17 modes.

There are three ways to start using Boost.PFR hashing, comparison and streaming operators for type T in your code. Each method has it's own drawbacks and suits own cases.

Table 1.1. Different approaches for operators

Approach

Defines operators in global namespace

Defined operators could be found by ADL

Works for local types

Usable localy, without affecting code from other scopes

Ignores implicit conversion operators

Respects user defined operators

using namespace boost::pfr::ops;

using namespace boost::pfr::flat_ops;

no

no

yes

yes

no

yes

BOOST_PFR_FLAT_FUNCTIONS_FOR

BOOST_PFR_PRECISE_FUNCTIONS_FOR

yes if T is in it

yes

no

no, but could be limited to translation unit

yes for T

no (compile time error)

boost/pfr/flat/global_ops.hpp

boost/pfr/precise/global_ops.hpp

yes

yes

yes

no, but could be limited to translation unit

yes all

yes


More detailed description follows:

1. using namespace boost::pfr::ops; and using namespace boost::pfr::flat_ops; approach

This method is good if you're writing generic algorithms and need to use operators from Boost.PFR only if there's no operators defined for the type:

#include <boost/pfr/precise/ops.hpp>

template <class T>
struct uniform_comparator_less {
    bool operator()(const T& lhs, const T& rhs) const noexcept {
        using namespace boost::pfr::ops;    // Enables Boost.PFR operators usage in this scope.

        // If T has operator< or conversion operator then will use it.
        // Otherwise will use boost::pfr::flat_less<T>.
        return lhs < rhs;
    }
};

This method's effects are local to the function. It works even for local types, like structures defined in functions. However Argument Dependant Lookup does not work with it:

#include <boost/pfr/flat/ops.hpp>
template <class T>
struct uniform_comparator_less {
    bool operator()(const T& lhs, const T& rhs) const noexcept {
        using namespace flat_ops;

        // Compile time error if T has neither operator< nor
        // conversion operator to comparable type.
        return std::less{}(lhs, rhs);
    }
};

2. BOOST_PFR_FLAT_FUNCTIONS_FOR and BOOST_PFR_PRECISE_FUNCTIONS_FOR approach

This method is good if you're writing POD structure and wish to define operators for that structure.

#include <boost/pfr/flat/functions_for.hpp>

struct pair_like {
    int first;
    short second;
};

BOOST_PFR_FLAT_FUNCTIONS_FOR(pair_like)   // Defines operators

// ...

assert(pair_like{1, 2} < pair_like{1, 3});

Argument Dependant Lookup works well, std::less will find the operators for struct pair_like. BOOST_PFR_FLAT_FUNCTIONS_FOR(T) can not be used for local types, it must be called only once in namespace of T. It does not respect conversion operators of T, so for example the following code will output different values:

#include <boost/pfr/flat/functions_for.hpp>

struct empty {
    operator std::string() { return "empty{}"; }
};
// Uncomment to get different output:
// BOOST_PFR_FLAT_FUNCTIONS_FOR(empty)

// ...
std::cout << empty{}; // Outputs `empty{}` if BOOST_PFR_FLAT_FUNCTIONS_FOR(empty) is commented out, '{}' otherwise.

3. boost/pfr/flat/global_ops.hpp and boost/pfr/precise/global_ops.hpp approach

This approach is for those, who wish to have comparisons/streaming/hashing for all their types.

#include <boost/pfr/flat/global_ops.hpp>

struct pair_like {
    int first;
    short second;
};

// ...

assert(pair_like{1, 2} < pair_like{1, 3});

Argument Dependant Lookup works well, std::less will find the operators for struct pair_like. Operators for local types will be also defined. All conversion operators of all POD types won't be used during comparisons/streaming/hashing.

With precise reflection you could do reflection if a type contains union. But be sure that operations for union are manually defined:

#include <boost/pfr/precise/ops.hpp>

union test_union {
    int i;
    float f;
};

inline bool operator==(test_union l, test_union r) noexcept; // Compile time error without this operator


struct foo { int i; test_union u; };

bool some_function(foo f1, foo f2) {
    using namespace boost::pfr::ops;
    return f1 == f2; // OK
}

Flat reflection of types that contain unions is disabled. Flat and precise reflection of unions is disabled for safety reasons. There's a way to reflect the first member of a union and use it. Unfortunately there's no way to find out active member of a union. Accessing an inactive union member is an Undefined Behavior. Using the first union member for reflection could lead to disaster if it is some character pointer. For example ostreaming union {char* c; long long ll; } u; u.ll= 1; will crash your program, as the active member is ll that holds 1 but we are trying to output a char*. This would cause an invalid pointer2 dereference.

Any attempt to reflect unions leads to a compile time error. In many cases a static assert is triggered that outputs the following message:

error: static_assert failed "====================> Boost.PFR: For safety reasons it is forbidden
        to reflect unions. See `Reflection of unions` section in the docs for more info."

PrevUpHomeNext