Add code for "Hourglass Interfaces for C++ APIs".

pull/1/head
Stefanus Du Toit 11 years ago
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…
Cancel
Save