Skip to content
Merged
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
112 changes: 112 additions & 0 deletions include/functional.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#pragma once

#include "traits.h"

namespace ink::runtime::internal
{
class basic_eval_stack;

// base function container with virtual callback methods
class function_base
{
public:
virtual ~function_base() { }

// calls the underlying function object taking parameters from a stack
virtual void call(basic_eval_stack* stack, size_t length) = 0;

protected:
// used to hide basic_eval_stack and value definitions
template<typename T>
static T pop(basic_eval_stack* stack);

// used to hide basic_eval_stack and value definitions
template<typename T>
static void push(basic_eval_stack* stack, const T& value);
};

// Stores a Callable function object and forwards calls to it
template<typename F>
class function : public function_base
{
public:
function(F functor) : functor(functor) { }

// calls the underlying function using arguments on the stack
virtual void call(basic_eval_stack* stack, size_t length) override
{
call(stack, length, GenSeq<traits::arity>());
}

private:
// Callable functor object
F functor;

// function traits
using traits = function_traits<F>;

// argument types
template<int index>
using arg_type = typename function_traits<F>::argument<index>::type;

// pops an argument from the stack using the function-type
template<int index>
typename arg_type<index> pop_arg(basic_eval_stack* stack)
{
// todo - type assert?

return pop<arg_type<index>>(stack);
}

template<size_t... Is>
void call(basic_eval_stack* stack, size_t length, seq<Is...>)
{
// Make sure the argument counts match
inkAssert(sizeof...(Is) == length, "Attempting to call functor with too few/many arguments");
static_assert(sizeof...(Is) == traits::arity);

// void functions
if constexpr (is_same<void, traits::return_type>::value)
{
// Just evalulate
functor(pop_arg<Is>(stack)...);

// Ink expects us to push something
// TODO -- Should be a special "void" value
push(stack, 0);
}
else
{
// Evaluate and push the result onto the stack
push(stack, functor(pop_arg<Is>(stack)...));
}
}
};

#ifdef INK_ENABLE_UNREAL
template<typename D>
class function_array_delegate : public function_base
{
public:
function_array_delegate(const D& del) : invocableDelegate(del) { }

// calls the underlying delegate using arguments on the stack
virtual void call(basic_eval_stack* stack, size_t length) override
{
// Create variable array
TArray<FInkVar> variables;
for (size_t i = 0; i < length; i++)
{
variables.Add(pop<FInkVar>(stack));
}

FInkVar result;
invocableDelegate.ExecuteIfBound(variables, result);

push(stack, result);
}
private:
D invocableDelegate;
};
#endif
}
29 changes: 29 additions & 0 deletions include/runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "config.h"
#include "system.h"
#include "functional.h"

#ifdef INK_ENABLE_UNREAL
#include "Containers/UnrealString.h"
Expand Down Expand Up @@ -127,6 +128,34 @@ namespace ink::runtime
* @param index index of the choice to make
*/
virtual void choose(size_t index) = 0;

protected:
// internal bind implementation. not for calling.
virtual void internal_bind(hash_t name, internal::function_base* function) = 0;
public:
/**
* Binds an external callable to the runtime
*
* Given a name and a callable object, register this function
* to be called back from the ink runtime.
*
* @param name name hash
* @param function callable
*/
template<typename F>
void bind(hash_t name, F function)
{
internal_bind(name, new internal::function(function));
}

#ifdef INK_ENABLE_UNREAL
template<typename D>
void bind_delegate(hash_t name, D functionDelegate)
{
internal_bind(name, new internal::function_array_delegate(functionDelegate));
}
#endif

#pragma endregion

#pragma region Convenience Methods
Expand Down
14 changes: 13 additions & 1 deletion include/story_ptr.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,14 @@ namespace ink::runtime
: story_ptr_base(nullptr, nullptr)
, _ptr(nullptr)
{
assert(ptr == nullptr, "can not create story_ptr from existing pointer!");
inkAssert(ptr == nullptr, "can not create story_ptr from existing pointer!");
}

// null constructor
story_ptr()
: story_ptr_base(nullptr, nullptr)
, _ptr(nullptr)
{
}

// destructor
Expand All @@ -105,7 +112,12 @@ namespace ink::runtime
story_ptr<U> cast()
{
// if cast fails, return null
#ifdef INK_ENABLE_UNREAL
// Unreal disables RTTI
U* casted = reinterpret_cast<U*>(_ptr);
#else
U* casted = dynamic_cast<U*>(_ptr);
#endif
if (casted == nullptr)
return nullptr;

Expand Down
116 changes: 116 additions & 0 deletions include/traits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#pragma once

namespace ink::runtime::internal
{
template<unsigned int N, typename Arg, typename... Args>
struct get
{
using type = typename get<N - 1, Args...>::type;
};

template<typename Arg, typename... Args>
struct get<0, Arg, Args...>
{
using type = typename Arg;
};

// constant and is_same from http://www.cppreference.com

template<typename T, T v>
struct constant {
static constexpr T value = v;
typedef T value_type;
typedef constant type; // using injected-class-name
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; } //since c++14
};

template<class T, class U>
struct is_same : constant<bool, false> {};

template<class T>
struct is_same<T, T> : constant<bool, true> {};

// function_traits from https://functionalcpp.wordpress.com/2013/08/05/function-traits/

template<class F>
struct function_traits;

// function pointer
template<class R, class... Args>
struct function_traits<R(*)(Args...)> : public function_traits<R(Args...)>
{};

template<class R, class... Args>
struct function_traits<R(Args...)>
{
using return_type = R;

static constexpr unsigned int arity = sizeof...(Args);

template <unsigned int N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename get<N, Args...>::type;
};
};

// member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...)> : public function_traits<R(C&, Args...)>
{};

// const member function pointer
template<class C, class R, class... Args>
struct function_traits<R(C::*)(Args...) const> : public function_traits<R(C&, Args...)>
{};

// member object pointer
template<class C, class R>
struct function_traits<R(C::*)> : public function_traits<R(C&)>
{};

// functor
template<class F>
struct function_traits
{
private:
using call_type = function_traits<decltype(&F::operator())>;
public:
using return_type = typename call_type::return_type;

static constexpr unsigned int arity = call_type::arity - 1;

template <unsigned int N>
struct argument
{
static_assert(N < arity, "error: invalid parameter index.");
using type = typename call_type::template argument<N + 1>::type;
};
};

// from https://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;

template<unsigned...> struct seq { using type = seq; };

template<class S1, class S2> struct concat;

template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1) + I2)...> {};

template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;

template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;

template<unsigned N>
struct gen_seq : Concat<GenSeq<N / 2>, GenSeq<N - N / 2>> {};

template<> struct gen_seq<0> : seq<> {};
template<> struct gen_seq<1> : seq<0> {};
}
7 changes: 7 additions & 0 deletions inkcpp/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,19 @@ namespace ink
MAX,
BINARY_OPERATORS_END = MAX,

// == Unary operators
UNARY_OPERATORS_START,
NOT = UNARY_OPERATORS_START,
NEGATE,
UNARY_OPERATORS_END = NEGATE,

// == Container tracking
START_CONTAINER_MARKER,
END_CONTAINER_MARKER,

// == Function calls
CALL_EXTERNAL,

NUM_COMMANDS,
};

Expand Down Expand Up @@ -151,6 +156,8 @@ namespace ink
"!",
"~",

nullptr,

nullptr,
nullptr
};
Expand Down
15 changes: 14 additions & 1 deletion inkcpp/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ namespace ink {
write_path(data, Command::DIVERT, path, self, flag);
}
}
else if (iter->find("^->") != iter->end())
else if (iter->find("^->") != iter->end()) // divert to value
{
// Get the divert path
auto path = (*iter)["^->"].get<std::string>();
Expand Down Expand Up @@ -352,6 +352,19 @@ namespace ink {
// Write out path. Speciically, we want the post-processor to write out the counter index for this container
write_path(data, Command::READ_COUNT, path, self, CommandFlag::NO_FLAGS, true);
}
// external function call
else if (iter->find("x()") != iter->end())
{
// Get name and argument count
auto name = (*iter)["x()"].get<std::string>();
int numArgs =
iter->find("exArgs") == iter->end()
? 0
: (*iter)["exArgs"].get<int>();

// Encode num arguments into command flag and write out the hash of the function name as the parameter
write(data, Command::CALL_EXTERNAL, hash_string(name.c_str()), (CommandFlag)numArgs);
}
}
}

Expand Down
Loading