mirror of https://github.com/CppCon/CppCon2014.git
Add code for "Hourglass Interfaces for C++ APIs".
parent
b71108a67c
commit
aa14d78a3a
@ -0,0 +1,10 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0.0)
|
||||||
|
project(hairpoll)
|
||||||
|
|
||||||
|
add_library(hairpoll SHARED hairpoll.cpp)
|
||||||
|
target_compile_options(hairpoll PRIVATE -std=c++1y)
|
||||||
|
target_compile_options(hairpoll PRIVATE -fvisibility=hidden)
|
||||||
|
|
||||||
|
add_executable(poller poller.cpp)
|
||||||
|
target_compile_options(poller PRIVATE -std=c++1y)
|
||||||
|
target_link_libraries(poller hairpoll)
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
This folder contains the example code for the CppCon 2014 presentation
|
||||||
|
"Hourglass interfaces for C++ APIs".
|
||||||
|
|
||||||
|
The example code in this folder is placed in the public domain and is
|
||||||
|
therefore free of copyright restrictions.
|
||||||
|
|
||||||
|
The code has been built and run under Mac OS X using the Clang compiler,
|
||||||
|
and can be built with `cmake . && make`.
|
||||||
|
|
||||||
|
-- Stefanus Du Toit, September 2014
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
typedef enum motu_alignment {
|
||||||
|
heroic_warrior = 0,
|
||||||
|
evil_warrior = 1,
|
||||||
|
} motu_alignment;
|
||||||
|
|
||||||
|
int32_t motu_count(motu_alignment a);
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
#include "hairpoll.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
struct error {
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* error_message(error_t error)
|
||||||
|
{
|
||||||
|
return error->message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void error_destruct(error_t error)
|
||||||
|
{
|
||||||
|
delete error;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if fn executed without throwing an error, false otherwise.
|
||||||
|
/// If calling fn threw an error, capture it in *out_error.
|
||||||
|
template<typename Fn>
|
||||||
|
bool translateExceptions(error_t* out_error, Fn&& fn)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
fn();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
*out_error = new error{e.what()};
|
||||||
|
return false;
|
||||||
|
} catch (...) {
|
||||||
|
*out_error = new error{"Unknown internal error"};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// hairpoll-related bits
|
||||||
|
///
|
||||||
|
|
||||||
|
class Poll {
|
||||||
|
public:
|
||||||
|
Poll(std::string person)
|
||||||
|
: person(person)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct option {
|
||||||
|
option(std::string name, std::string url)
|
||||||
|
: name(name)
|
||||||
|
, url(url)
|
||||||
|
, votes(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string url;
|
||||||
|
int votes;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string person;
|
||||||
|
std::vector<option> options;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
struct hairpoll {
|
||||||
|
template<typename... Args>
|
||||||
|
hairpoll(Args&&... args)
|
||||||
|
: actual(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll actual;
|
||||||
|
};
|
||||||
|
|
||||||
|
hairpoll_t hairpoll_construct(const char* person, error_t* out_error)
|
||||||
|
{
|
||||||
|
hairpoll_t result = nullptr;
|
||||||
|
translateExceptions(out_error, [&]{
|
||||||
|
result = std::make_unique<hairpoll>(person).release();
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hairpoll_destruct(hairpoll_t poll)
|
||||||
|
{
|
||||||
|
delete poll;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t hairpoll_add_option(hairpoll_t poll, const char* name, const char* image_url, error_t* out_error)
|
||||||
|
{
|
||||||
|
int32_t result = -1;
|
||||||
|
|
||||||
|
translateExceptions(out_error, [&]{
|
||||||
|
poll->actual.options.emplace_back(name, image_url);
|
||||||
|
result = static_cast<int32_t>(poll->actual.options.size() - 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hairpoll_vote(const hairpoll_t poll, int32_t option, error_t* out_error)
|
||||||
|
{
|
||||||
|
translateExceptions(out_error, [&]{
|
||||||
|
if (option < 0 || option >= poll->actual.options.size()) {
|
||||||
|
throw std::runtime_error("Option index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
poll->actual.options[option].votes++;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void hairpoll_tally(const hairpoll_t hairpoll, hairpoll_result_handler_t handler, void* client_data,
|
||||||
|
error_t* out_error)
|
||||||
|
{
|
||||||
|
translateExceptions(out_error, [&]{
|
||||||
|
for (auto&& option : hairpoll->actual.options) {
|
||||||
|
auto html = std::string("<img src=\"")
|
||||||
|
+ option.url
|
||||||
|
+ "\" /> - "
|
||||||
|
+ std::to_string(option.votes);
|
||||||
|
handler(client_data, option.name.c_str(), option.votes, html.c_str());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "visibility.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// Error handling
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef struct error* error_t;
|
||||||
|
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
const char* error_message(error_t error);
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
void error_destruct(error_t error);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Hair polls
|
||||||
|
//
|
||||||
|
|
||||||
|
typedef struct hairpoll* hairpoll_t;
|
||||||
|
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
hairpoll_t hairpoll_construct(const char* person, error_t* out_error);
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
void hairpoll_destruct(hairpoll_t poll);
|
||||||
|
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
int32_t hairpoll_add_option(hairpoll_t hairpoll, const char* name, const char* image_url, error_t* out_error);
|
||||||
|
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
void hairpoll_vote(hairpoll_t hairpoll, int32_t option, error_t* out_error);
|
||||||
|
|
||||||
|
typedef void (*hairpoll_result_handler_t)(void* client_data, const char* name, int32_t votes, const char* html);
|
||||||
|
|
||||||
|
HAIRPOLL_EXPORT
|
||||||
|
void hairpoll_tally(const hairpoll_t hairpoll, hairpoll_result_handler_t handler, void* client_data,
|
||||||
|
error_t* out_error);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} // extern "C"
|
||||||
|
#endif
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
lib = CDLL("libhairpoll.dylib")
|
||||||
|
lib.hairpoll_construct.restype = c_void_p
|
||||||
|
lib.hairpoll_construct.argtypes = [c_char_p, c_void_p]
|
||||||
|
|
||||||
|
lib.hairpoll_destruct.restype = None
|
||||||
|
lib.hairpoll_destruct.argtypes = [c_void_p]
|
||||||
|
|
||||||
|
lib.hairpoll_add_option.restype = c_int
|
||||||
|
lib.hairpoll_add_option.argtypes = [c_void_p, c_char_p, c_char_p, c_void_p]
|
||||||
|
|
||||||
|
lib.hairpoll_vote.restype = None
|
||||||
|
lib.hairpoll_vote.argtypes = [c_void_p, c_int, c_void_p]
|
||||||
|
|
||||||
|
hairpoll_result_handler = CFUNCTYPE(None, c_void_p, c_char_p, c_int, c_char_p)
|
||||||
|
lib.hairpoll_tally.restype = None
|
||||||
|
lib.hairpoll_tally.argtypes = [c_void_p, hairpoll_result_handler, c_void_p, c_void_p]
|
||||||
|
|
||||||
|
hairpoll = lib.hairpoll_construct("Stefanus Du Toit", None)
|
||||||
|
|
||||||
|
skeletor = lib.hairpoll_add_option(hairpoll, "Skeletor",
|
||||||
|
"http://static.comicvine.com/uploads/original/4/49448/2413657-skeletor.jpg", None)
|
||||||
|
beast = lib.hairpoll_add_option(hairpoll, "Beast Man",
|
||||||
|
"https://angryjedi.files.wordpress.com/2010/10/he-man_beast_man.jpeg", None)
|
||||||
|
|
||||||
|
lib.hairpoll_vote(hairpoll, skeletor, None)
|
||||||
|
lib.hairpoll_vote(hairpoll, beast, None)
|
||||||
|
lib.hairpoll_vote(hairpoll, beast, None)
|
||||||
|
|
||||||
|
def print_result(client_data, name, votes, html):
|
||||||
|
print name, votes
|
||||||
|
|
||||||
|
lib.hairpoll_tally(hairpoll, hairpoll_result_handler(print_result), None, None)
|
||||||
|
|
||||||
|
lib.hairpoll_destruct(hairpoll)
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
#include "hairpoll.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Error handling
|
||||||
|
//
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
Error()
|
||||||
|
: opaque(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Error()
|
||||||
|
{
|
||||||
|
if (opaque) {
|
||||||
|
error_destruct(opaque);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_t opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThrowOnError {
|
||||||
|
public:
|
||||||
|
~ThrowOnError() noexcept(false)
|
||||||
|
{
|
||||||
|
if (_error.opaque) {
|
||||||
|
throw std::runtime_error(error_message(_error.opaque));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator error_t*() { return &_error.opaque; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Error _error;
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// HairPoll related functionality
|
||||||
|
//
|
||||||
|
|
||||||
|
class HairPoll {
|
||||||
|
public:
|
||||||
|
HairPoll(std::string person)
|
||||||
|
: _opaque(hairpoll_construct(person.c_str(), ThrowOnError{}))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~HairPoll()
|
||||||
|
{
|
||||||
|
hairpoll_destruct(_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
HairPoll(const HairPoll&) = delete;
|
||||||
|
HairPoll& operator=(const HairPoll&) = delete;
|
||||||
|
|
||||||
|
int addOption(std::string name, std::string imageUrl)
|
||||||
|
{
|
||||||
|
return hairpoll_add_option(_opaque, name.c_str(), imageUrl.c_str(), ThrowOnError{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void vote(int option)
|
||||||
|
{
|
||||||
|
return hairpoll_vote(_opaque, option, ThrowOnError{});
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Result {
|
||||||
|
std::string name;
|
||||||
|
int votes;
|
||||||
|
std::string html;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Result> results() const
|
||||||
|
{
|
||||||
|
std::vector<Result> ret;
|
||||||
|
|
||||||
|
auto addResult = [&ret](const char* name, int32_t votes, const char* html){
|
||||||
|
ret.push_back(Result{name, votes, html});
|
||||||
|
};
|
||||||
|
|
||||||
|
auto callback = [](void* client_data, const char* name, int32_t votes, const char* html){
|
||||||
|
auto fn = static_cast<decltype(&addResult)>(client_data);
|
||||||
|
(*fn)(name, votes, html);
|
||||||
|
};
|
||||||
|
|
||||||
|
hairpoll_tally(_opaque, callback, &addResult, ThrowOnError{});
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
hairpoll_t _opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
void conductPoll()
|
||||||
|
{
|
||||||
|
HairPoll poll("Stefanus Du Toit");
|
||||||
|
|
||||||
|
int skel = poll.addOption("Skeletor", "http://static.comicvine.com/uploads/original/4/49448/2413657-skeletor.jpg");
|
||||||
|
int beast = poll.addOption("Beast Man", "https://angryjedi.files.wordpress.com/2010/10/he-man_beast_man.jpeg");
|
||||||
|
|
||||||
|
poll.vote(skel);
|
||||||
|
poll.vote(beast);
|
||||||
|
poll.vote(beast);
|
||||||
|
|
||||||
|
// Uncomment this line to exercise the error handling case.
|
||||||
|
// poll.vote(423);
|
||||||
|
|
||||||
|
std::cout << "<!DOCTYPE html>\n"
|
||||||
|
<< "<html>\n"
|
||||||
|
<< "<body>\n"
|
||||||
|
<< "<ul>\n";
|
||||||
|
for (auto&& result : poll.results()) {
|
||||||
|
std::cout << "<li>" << result.html << "</li>\n";
|
||||||
|
}
|
||||||
|
std::cout << "</ul>\n"
|
||||||
|
<< "</body>\n"
|
||||||
|
<< "</html>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
conductPoll();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error during poll: " << e.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||||
|
#ifdef hairpoll_EXPORTS
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define HAIRPOLL_EXPORT __attribute__ ((dllexport))
|
||||||
|
#else
|
||||||
|
#define HAIRPOLL_EXPORT __declspec(dllexport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#define HAIRPOLL_EXPORT __attribute__ ((dllimport))
|
||||||
|
#else
|
||||||
|
#define HAIRPOLL_EXPORT __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#if __GNUC__ >= 4
|
||||||
|
#define HAIRPOLL_EXPORT __attribute__ ((visibility ("default")))
|
||||||
|
#else
|
||||||
|
#define HAIRPOLL_EXPORT
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue