1# Mapbox Variant 2 3An header-only alternative to `boost::variant` for C++11 and C++14 4 5[](https://travis-ci.org/mapbox/variant) 6[](https://ci.appveyor.com/project/Mapbox/variant) 7[](https://coveralls.io/r/mapbox/variant?branch=master) 8 9## Introduction 10 11Variant's basic building blocks are: 12 13- `variant<Ts...>` - a type-safe representation for sum-types / discriminated unions 14- `recursive_wrapper<T>` - a helper type to represent recursive "tree-like" variants 15- `apply_visitor(visitor, myVariant)` - to invoke a custom visitor on the variant's underlying type 16- `get<T>()` - a function to directly unwrap a variant's underlying type 17- `.match([](Type){})` - a variant convenience member function taking an arbitrary number of lambdas creating a visitor behind the scenes and applying it to the variant 18 19### Basic Usage - HTTP API Example 20 21Suppose you want to represent a HTTP API response which is either a JSON result or an error: 22 23```c++ 24struct Result { 25 Json object; 26}; 27 28struct Error { 29 int32_t code; 30 string message; 31}; 32``` 33 34You can represent this at type level using a variant which is either an `Error` or a `Result`: 35 36```c++ 37using Response = variant<Error, Result>; 38 39Response makeRequest() { 40 return Error{501, "Not Implemented"}; 41} 42 43Response ret = makeRequest(); 44``` 45 46To see which type the `Response` holds you pattern match on the variant unwrapping the underlying value: 47 48```c++ 49ret.match([] (Result r) { print(r.object); }, 50 [] (Error e) { print(e.message); }); 51``` 52 53Instead of using the variant's convenience `.match` pattern matching function you can create a type visitor functor and use `apply_visitor` manually: 54 55```c++ 56struct ResponseVisitor { 57 void operator()(Result r) const { 58 print(r.object); 59 } 60 61 void operator()(Error e) const { 62 print(e.message); 63 } 64}; 65 66ResponseVisitor visitor; 67apply_visitor(visitor, ret); 68``` 69 70In both cases the compiler makes sure you handle all types the variant can represent at compile. 71 72### Recursive Variants - JSON Example 73 74[JSON](http://www.json.org/) consists of types `String`, `Number`, `True`, `False`, `Null`, `Array` and `Object`. 75 76```c++ 77struct String { string value; }; 78struct Number { double value; }; 79struct True { }; 80struct False { }; 81struct Null { }; 82struct Array { vector<?> values; }; 83struct Object { unordered_map<string, ?> values; }; 84``` 85 86This works for primitive types but how do we represent recursive types such as `Array` which can hold multiple elements and `Array` itself, too? 87 88For these use cases Variant provides a `recursive_wrapper` helper type which lets you express recursive Variants. 89 90```c++ 91struct String { string value; }; 92struct Number { double value; }; 93struct True { }; 94struct False { }; 95struct Null { }; 96 97// Forward declarations only 98struct Array; 99struct Object; 100 101using Value = variant<String, Number, True, False, Null, recursive_wrapper<Array>, recursive_wrapper<Object>>; 102 103struct Array { 104 vector<Value> values; 105}; 106 107struct Object { 108 unordered_map<string, Value> values; 109}; 110``` 111 112For walking the JSON representation you can again either create a `JSONVisitor`: 113 114```c++ 115struct JSONVisitor { 116 117 void operator()(Null) const { 118 print("null"); 119 } 120 121 // same for all other JSON types 122}; 123 124JSONVisitor visitor; 125apply_visitor(visitor, json); 126``` 127 128Or use the convenience `.match` pattern matching function: 129 130```c++ 131json.match([] (Null) { print("null"); }, 132 ...); 133``` 134 135To summarize: use `recursive_wrapper` to represent recursive "tree-like" representations: 136 137```c++ 138struct Empty { }; 139struct Node; 140 141using Tree = variant<Empty, recursive_wrapper<Node>>; 142 143struct Node { 144 uint64_t value; 145} 146``` 147 148### Advanced Usage Tips 149 150Creating type aliases for variants is a great way to reduce repetition. 151Keep in mind those type aliases are not checked at type level, though. 152We recommend creating a new type for all but basic variant usage: 153 154```c++ 155// the compiler can't tell the following two apart 156using APIResult = variant<Error, Result>; 157using FilesystemResult = variant<Error, Result>; 158 159// new type 160struct APIResult : variant<Error, Result> { 161 using Base = variant<Error, Result>; 162 using Base::Base; 163} 164``` 165 166## Why use Mapbox Variant? 167 168Mapbox variant has the same speedy performance of `boost::variant` but is 169faster to compile, results in smaller binaries, and has no dependencies. 170 171For example on OS X 10.9 with clang++ and libc++: 172 173Test | Mapbox Variant | Boost Variant 174---- | -------------- | ------------- 175Size of pre-compiled header (release / debug) | 2.8/2.8 MB | 12/15 MB 176Size of simple program linking variant (release / debug) | 8/24 K | 12/40 K 177Time to compile header | 185 ms | 675 ms 178 179(Numbers from an older version of Mapbox variant.) 180 181## Goals 182 183Mapbox `variant` has been a very valuable, lightweight alternative for apps 184that can use c++11 or c++14 but that do not want a boost dependency. 185Mapbox `variant` has also been useful in apps that do depend on boost, like 186mapnik, to help (slightly) with compile times and to majorly lessen dependence 187on boost in core headers. The original goal and near term goal is to maintain 188external API compatibility with `boost::variant` such that Mapbox `variant` 189can be a "drop in". At the same time the goal is to stay minimal: Only 190implement the features that are actually needed in existing software. So being 191an "incomplete" implementation is just fine. 192 193Currently Mapbox variant doesn't try to be API compatible with the upcoming 194variant standard, because the standard is not finished and it would be too much 195work. But we'll revisit this decision in the future if needed. 196 197If Mapbox variant is not for you, have a look at [these other 198implementations](doc/other_implementations.md). 199 200Want to know more about the upcoming standard? Have a look at our 201[overview](doc/standards_effort.md). 202 203Most modern high-level languages provide ways to express sum types directly. 204If you're curious have a look at Haskell's pattern matching or Rust's and Swift's enums. 205 206## Depends 207 208- Compiler supporting `-std=c++11` or `-std=c++14` 209 210Tested with: 211 212- g++-4.7 213- g++-4.8 214- g++-4.9 215- g++-5.2 216- clang++-3.5 217- clang++-3.6 218- clang++-3.7 219- clang++-3.8 220- clang++-3.9 221- Visual Studio 2015 222 223## Unit Tests 224 225On Unix systems compile and run the unit tests with `make test`. 226 227On Windows run `scripts/build-local.bat`. 228 229## Limitations 230 231- The `variant` can not hold references (something like `variant<int&>` is 232 not possible). You might want to try `std::reference_wrapper` instead. 233 234## Deprecations 235 236- The included implementation of `optional` is deprecated and will be removed 237 in a future version. See [issue #64](https://github.com/mapbox/variant/issues/64). 238- Old versions of the code needed visitors to derive from `static_visitor`. 239 This is not needed any more and marked as deprecated. The `static_visitor` 240 class will be removed in future versions. 241 242## Benchmarks 243 244 make bench 245 246## Check object sizes 247 248 make sizes /path/to/boost/variant.hpp 249