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