Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip/cpp #4

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/testml-c++
1 change: 1 addition & 0 deletions bin/testml-cpp
1 change: 1 addition & 0 deletions bin/testml-cpp-tap
Empty file removed src/cpp/.gitignore
Empty file.
6 changes: 5 additions & 1 deletion src/cpp/bin/testml-cpp-tap
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ testml-run-file() {
set -x
/usr/bin/env c++ \
-o $testml_runner \
-std=c++14 -Wall -Wextra \
-I ext \
$src_bin \
$lib/run/tap.cpp \
$lib/run.cpp \
$lib/runtime.cpp \
$lib/stdlib.cpp \
$lib/bridge.cpp \
$lib/wrapper.cpp \
$lib/utils.cpp \
$bridge_file || return

chmod +x $testml_runner
Expand Down
1 change: 1 addition & 0 deletions src/cpp/ext
15 changes: 15 additions & 0 deletions src/cpp/lib/testml/bridge.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "bridge.hpp"

namespace testml {

using json = nlohmann::json;

json Bridge::call(std::string const& name, std::vector<json> const& args) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the call method belongs in the bridge base class. This implies that a user defined bridge method would want to call this method directly. Maybe this method should be called can and it returns the bridge method pointer of the requested method, if found.

auto it = _fns.find(name);
if (it == _fns.end()) {
throw std::runtime_error("Bridge method not found: " + name + ".");
}
return it->second->call(args);
}

}
112 changes: 112 additions & 0 deletions src/cpp/lib/testml/bridge.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#pragma once

#include <tuple>
#include <utility>
#include <unordered_map>
#include <memory>
#include <stdexcept>
#include <string>

#include "boost/callable_traits/args.hpp"
#include "boost/callable_traits/class_of.hpp"
#include "boost/callable_traits/return_type.hpp"
#include "nlohmann/json.hpp"

#include "utils.hpp"
#include "wrapper.hpp"

namespace testml {

namespace details {

using json = nlohmann::json;
using wrapper::cook;
using wrapper::uncook;

namespace ct = boost::callable_traits;

// we need this details class so that we can have a non-templated value
// stored in the Bridge _fns map.
struct FnHolder {
virtual json call(std::vector<json> const&) = 0;
};

// the implementation of a FnHolder, which keeps the types around
template<typename BridgeT, typename Fn>
class FnHolderImpl : public FnHolder {
Fn _fn;
BridgeT* _bridge;
static constexpr bool _is_pmf = std::is_member_function_pointer<Fn>::value;
using RawArg = ct::args_t<Fn>;
// in case of a PMF, remove the class type from the argument list
using Arg = std::conditional_t<_is_pmf, typename utils::remove_first_type<RawArg>::type, RawArg>;
using Ret = ct::return_type_t<Fn>;
static constexpr std::size_t _num_args = std::tuple_size<Arg>::value;

// type of the N-th argument that the stored function takes
template<std::size_t I>
using ArgType = typename std::tuple_element<I, Arg>::type;

// uncook each argument to its expected type, and call the function
// we do SFINAE in the return type, using comma+sizeof() to get a dependance on I.

// PMF case
template<std::size_t... I>
auto call_impl(std::vector<json> const& args, std::index_sequence<I...>)
-> typename std::enable_if<(sizeof...(I), _is_pmf), Ret>::type {
return (_bridge->*_fn)(uncook<ArgType<I>>(args[I])...);
}

// non-PMF case (BridgeT = nullptr_t)
template<std::size_t... I>
auto call_impl(std::vector<json> const& args, std::index_sequence<I...>)
-> typename std::enable_if<(sizeof...(I), !_is_pmf), Ret>::type {
return _fn(uncook<ArgType<I>>(args[I])...);
}

public:
FnHolderImpl(BridgeT* bridge, Fn fn)
: _fn{std::move(fn)},
_bridge{bridge} {
}

// check arity and call the function using our little helper, before wrapping it back to json
json call(std::vector<json> const& args) override {
if (args.size() != _num_args) {
throw std::runtime_error("Bridge method call with wrong arity, expected " + std::to_string(_num_args) + ", got " + std::to_string(args.size()) + ".");
}

return cook(call_impl(args, std::make_index_sequence<_num_args>{}));
}

};

}

class Bridge {
// store a wrapper FnHolder in the map, with FnHolderImpl to keep the correct types around and do FFI correctly
std::unordered_map<std::string, std::unique_ptr<details::FnHolder>> _fns;

public:
// PMF version, takes the object to call the function on
template<typename BridgeT, typename Fn>
auto bind(std::string const& name, BridgeT* obj, Fn fn)
-> typename std::enable_if<std::is_member_function_pointer<Fn>::value, void>::type {
static_assert(std::is_same<details::ct::class_of_t<Fn>, BridgeT>::value, "Bridge subclass must pass itself");

using HolderType = details::FnHolderImpl<BridgeT, Fn>;
_fns[name] = std::make_unique<HolderType>(obj, std::move(fn));
}

// any other candidate
template<typename Fn>
auto bind(std::string const& name, Fn fn)
-> typename std::enable_if<!std::is_member_function_pointer<Fn>::value, void>::type {
using HolderType = details::FnHolderImpl<std::nullptr_t, Fn>;
_fns[name] = std::make_unique<HolderType>(nullptr, std::move(fn));
}

nlohmann::json call(std::string const& name, std::vector<nlohmann::json> const& args);
};

}
16 changes: 0 additions & 16 deletions src/cpp/lib/testml/run.cpp

This file was deleted.

13 changes: 0 additions & 13 deletions src/cpp/lib/testml/run.h

This file was deleted.

39 changes: 32 additions & 7 deletions src/cpp/lib/testml/run/tap.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
#include <iostream>
#include <string>
#include "tap.h"
#include <iostream>
#include "tap.hpp"

namespace testml {
namespace run {

void TestML_Run_TAP::run(std::string file) {
TestML_Run_TAP tap;
void TAP::testml_eq(json want, json got, std::string const& label) {
if (want == got) {
tap_pass(label);
} else {
tap_fail(label);
}
}

tap.from_file(file);
void TAP::tap_pass(std::string const& label) {
std::cout << "ok " << ++count;
if (!label.empty()) {
std::cout << " - " << label;
}
std::cout << "\n";
}

std::cout << "1..1" << std::endl;
std::cout << "ok 1 - It worked" << std::endl;
void TAP::tap_fail(std::string const& label) {
std::cout << "not ok " << ++count;
if (!label.empty()) {
std::cout << " - " << label;
}
std::cout << "\n";
}

void TAP::testml_end() {
std::cout << "1.." << count;
}

}
}
12 changes: 0 additions & 12 deletions src/cpp/lib/testml/run/tap.h

This file was deleted.

28 changes: 28 additions & 0 deletions src/cpp/lib/testml/run/tap.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include "../runtime.hpp"

#include <string>

namespace testml {
namespace run {

class TAP : public Runtime {
using json = nlohmann::json;

using Runtime::Runtime;

protected:
void testml_eq(json want, json got, std::string const& label) override;
void testml_end() override;

private:
void tap_pass(std::string const& label);
void tap_fail(std::string const& label);

private:
int count = 0;
};

}
}
Loading