AssociatedEnum is a header-only library for C++ for enumerations with associated values
asenum is C++ implementation of very neat enums from Swift language (https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html, chapter 'Associated Values').
asenum combines Enum and Variant: it allows to create lighweight wrapper around plain C++ enum and add ability to assign different value types to different cases.
- each enum case can be associated with any type
- values are immutable: totally thread-safe
- simple interface
- lightweight, header-only, single file library
- requires only C++11
- convenient switch, map, equality, comparison
Simply copy file 'include/asenum/asenum.h' to your project.
// More examples you can find in tests
#include <asenum/asenum.h>
#include <string>
#include <chrono>
#include <iostream>
// ===== DECLARATION =====
enum class ErrorCode
{
Unknown,
Success,
Timeout
};
// Associate enum 'ErrorCode' with AsEnum 'AnyError'
using AnyError = asenum::AsEnum<
asenum::Case11<ErrorCode, ErrorCode::Unknown, std::string>,
asenum::Case11<ErrorCode, ErrorCode::Success, void>,
asenum::Case11<ErrorCode, ErrorCode::Timeout, std::chrono::seconds>
>;
// ===== USAGE =====
void LogError(const AnyError& error)
{
error.doSwitch()
.ifCase<ErrorCode::Unknown>([] (const std::string& value) {
std::cout << "Unknown error: " << value << "\n";
})
.ifCase<ErrorCode::Success>([] {
std::cout << "Success\n";
})
.ifCase<ErrorCode::Timeout>([] (const std::chrono::seconds& value) {
std::cout << "Timed out after: " << value.count() << "\n";
})
.ifDefault([] {
std::cout << "Default\n";
});
}
int main()
{
// ===== CREATION =====
LogError(AnyError::create<ErrorCode::Unknown>("test.api.com"));
LogError(AnyError::create<ErrorCode::Success>());
LogError(AnyError::create<ErrorCode::Timeout>(std::chrono::seconds(1)));
return 0;
}
AsEnum supports mapping to any desired type Note: if all cases covered by mapping, it doesn't require 'default' case
// ===== Mapping ======
std::string ErrorToString(const AnyError& error)
{
// All cases covered
const auto stringRepresentation = error.doMap<std::string>()
.ifCase<ErrorCode::Unknown>([] (const std::string& value) {
return value;
})
.ifCase<ErrorCode::Success>([] {
return "Success";
})
.ifCase<ErrorCode::Timeout>([] (const std::chrono::seconds& value) {
return "Timed out after: " + std::to_string(value.count());
});
return stringRepresentation;
}
// ===== Partial Mapping ======
std::string ErrorToString2(const AnyError& error)
{
// All cases covered
const auto stringRepresentation = error.doMap<std::string>()
.ifCase<ErrorCode::Success>([] {
return "Success";
})
.ifDefault([] {
return "Unknown error";
});
return stringRepresentation;
}
AsEnum provides native way of comparing values
void Equality(const AnyError& error1, const AnyError& error2)
{
// We can check if error1 == error2
// Cases differ => NOT Equal
// Cases same AND underlying values same => Equal
const bool equal = error1 == error2;
}
void Comparability(const AnyError& error1, const AnyError& error2)
{
// We can check how error1 relates to error2
// Cases of error1 and error2 differ => Compare case ordering
// Cases same => Compare underlying values
const bool less = error1 < error2;
const bool lessEqual = error1 <= error2;
const bool greater = error1 > error2;
const bool greaterEqual = error1 >= error2;
}
#include <asenum/asenum.h>
#include <string>
#include <chrono>
#include <iostream>
#include <cmath>
enum class RootsType
{
None,
Single,
Pair
};
using Roots = asenum::AsEnum<
asenum::Case11<RootsType, RootsType::None, void>,
asenum::Case11<RootsType, RootsType::Single, double>,
asenum::Case11<RootsType, RootsType::Pair, std::pair<double, double>>
>;
Roots FindRoots(const double a, const double b, const double c)
{
const double d = (b * b) - (4 * a * c);
if (d < 0)
{
return Roots::create<RootsType::None>();
}
const double x1 = (-b + sqrt(d)) / (2 * a);
const double x2 = (-b - sqrt(d)) / (2 * a);
return x1 == x2 ? Roots::create<RootsType::Single>(x1) : Roots::create<RootsType::Pair>(std::make_pair(x1, x2));
}
double ReadK(const std::string& name)
{
double k = 0;
std::cout << "Input " << name << ": ";
std::cin >> k;
return k;
}
int main()
{
const double a = ReadK("a");
const double b = ReadK("b");
const double c = ReadK("c");
const Roots roots = FindRoots(a, b, c);
roots.doSwitch()
.ifCase<RootsType::None>([] {
std::cout << "No roots\n";
})
.ifCase<RootsType::Single>([] (const double x) {
std::cout << "Single (equal) roots: " << x;
})
.ifCase<RootsType::Pair>([] (const std::pair<double, double>& roots) {
std::cout << "Different roots. x1 = " << roots.first << "; x2 = " << roots.second << ".\n";
});
return 0;
}
#include <asenum/asenum.h>
#include <string>
#include <vector>
enum class BarCodeType
{
UPC,
QrCode
};
using BarCode = asenum::AsEnum<
asenum::Case11<BarCodeType, BarCodeType::UPC, std::vector<uint8_t>>,
asenum::Case11<BarCodeType, BarCodeType::QrCode, std::vector<uint8_t>>>
>;
std::string ConvertUPCToLink(const std::vector<uint8_t>& photo)
{
std::string link;
// perform some computations...
return link;
}
std::string ConvertQrCodeToLink(const std::vector<uint8_t>& photo)
{
std::string link;
// perform some computations...
return link;
}
std::string ConvertBarCodeToLink(const BarCode& barCode)
{
return barCode.doMap<std::string>()
.ifCase<BarCodeType::UPC>(ConvertUPCToLink)
.ifCase<BarCodeType::QrCode>(ConvertQrCodeToLink);
}
enum class DownloadResultType
{
Data,
Error,
Canceled
};
using DownloadResult = asenum::AsEnum<
asenum::Case11<DownloadResultType, DownloadResultType::Data, std::string>,
asenum::Case11<DownloadResultType, DownloadResultType::Error, int>,
asenum::Case11<DownloadResultType, DownloadResultType::Canceled, void>
>;
DownloadResult DownloadFileFromLink(const std::string& link)
{
// perform HTTP(s) request
// return response data on success, ...
return DownloadResult::create<DownloadResultType::Data>("...");
// ... or return server error, ...
return DownloadResult::create<DownloadResultType::Error>(404);
// ... or inform caller that request has been canceled.
return DownloadResult::create<DownloadResultType::Canceled>();
}
int main()
{
const DownloadResult result = DownloadFileFromLink("http://test.api.com/download/file1.txt");
result.doSwitch()
.ifCase<DownloadResultType::Data>([] (const std::string& content) {
std::cout << "Downloaded file content: " << content << "\n";
})
.ifCase<DownloadResultType::Error>([] (const int errorCode) {
std::cout << "Request did fail with error: " << errorCode << "\n";
})
.ifCase<DownloadResultType::Canceled>([] {
std::cout << "Request has been canceled by user\n";
});
return 0;
}