Skip to content

Instantly share code, notes, and snippets.

@artemkin
Forked from porglezomp/Makefile
Last active September 26, 2019 07:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save artemkin/4bc3aab3d5901bf2d7d8603c3a7f8834 to your computer and use it in GitHub Desktop.
Save artemkin/4bc3aab3d5901bf2d7d8603c3a7f8834 to your computer and use it in GitHub Desktop.
Multiple return types

It's possible to return multiple types from a function in C++ by using exceptions! Unfortunately, it turns out that C++ exceptions are very slow. How slow? Well…

  • variant and union both run in about 7ms
  • exn runs in about 1010ms

So using exceptions as control flow in C++ is about 140x slower.

#include <string>
#include <chrono>
#include <iostream>
using Clock = std::chrono::high_resolution_clock;
void return_item(int counter) {
if (counter % 2 == 0) {
throw counter;
} else {
throw std::string("hey!");
}
}
int main() {
int total = 0, total2 = 0;
auto start = Clock::now();
for (int i = 0; i < 1000000; i++) {
try {
return_item(i);
} catch (int i) {
total += i;
} catch (std::string s) {
total2 += s.size();
}
}
auto end = Clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
std::cout << total << " " << total2 << " in " << ns << "ns" << std::endl;
return 0;
}
all: union exn variant
%: %.cpp
clang++ -Wall -Wextra -Werror -std=c++17 -O3 $< -o $@
clean:
rm -f union exn variant
#include <string>
#include <chrono>
#include <iostream>
using Clock = std::chrono::high_resolution_clock;
enum Tag {
STRING,
INT,
};
struct Result {
Tag tag;
union {
std::string str;
int num;
};
Result(const Result&) = delete;
const Result& operator=(const Result&) = delete;
Result(std::string value) : tag(STRING), str(std::move(value)) {}
Result(int value) : tag(INT), num(value) {}
~Result() {
switch (tag) {
case STRING:
str.~basic_string();
break;
case INT: break;
}
}
};
Result return_item(int counter) {
if (counter % 2 == 0) {
return Result(counter);
} else {
return Result("hey!");
}
}
int main() {
int total = 0, total2 = 0;
auto start = Clock::now();
for (int i = 0; i < 1000000; i++) {
Result item = return_item(i);
switch (item.tag) {
case STRING:
total2 += item.str.size();
break;
case INT:
total += item.num;
break;
}
}
auto end = Clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
std::cout << total << " " << total2 << " in " << ns << "ns" << std::endl;
return 0;
}
#include <string>
#include <chrono>
#include <iostream>
#include <variant>
using Clock = std::chrono::high_resolution_clock;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
std::variant<int, std::string> return_item(int counter) {
if (counter % 2 == 0) {
return counter;
} else {
return "hey!";
}
}
int main() {
int total = 0, total2 = 0;
auto start = Clock::now();
for (int i = 0; i < 1000000; i++) {
auto item = return_item(i);
std::visit(overloaded {
[&](int i) { total += i; },
[&](std::string s) { total2 += s.size(); },
}, item);
}
auto end = Clock::now();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
std::cout << total << " " << total2 << " in " << ns << "ns" << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment