Skip to content

Commit

Permalink
Updated Get method to support multiple keys
Browse files Browse the repository at this point in the history
- Values can now be fetched from Lua stack, in batches, either by index, name or both.
  • Loading branch information
mrcoalp committed Mar 6, 2021
1 parent edc6e88 commit 27161be
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 94 deletions.
121 changes: 73 additions & 48 deletions include/moon/moon.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@

#define MOON_DECLARE_CLASS(_class) static moon::Binding<_class> Binding;

#define MOON_PROPERTY(_property) \
int Get_##_property(lua_State* L) { \
Moon::Push(_property); \
return 1; \
} \
int Set_##_property(lua_State* L) { \
_property = Moon::Get<decltype(_property)>(); \
return 0; \
#define MOON_PROPERTY(_property) \
int Get_##_property(lua_State* L) { \
Moon::Push(_property); \
return 1; \
} \
int Set_##_property(lua_State* L) { \
_property = Moon::Get<decltype(_property)>(1); \
return 0; \
}

#define MOON_METHOD(_name) int _name(lua_State* L)
Expand All @@ -45,19 +45,19 @@
namespace moon {
namespace meta {
template <typename T, typename Ret = T>
using is_bool_t = std::enable_if_t<std::is_same_v<T, bool>, Ret>;
using is_bool_t = std::enable_if_t<std::is_same_v<std::decay_t<T>, bool>, Ret>;

template <typename T, typename Ret = T>
using is_integral_t = std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, Ret>;
using is_integral_t = std::enable_if_t<std::is_integral_v<std::decay_t<T>> && !std::is_same_v<T, bool>, Ret>;

template <typename T, typename Ret = T>
using is_floating_point_t = std::enable_if_t<std::is_floating_point_v<T>, Ret>;
using is_floating_point_t = std::enable_if_t<std::is_floating_point_v<std::decay_t<T>>, Ret>;

template <typename T, typename Ret = T>
using is_string_t = std::enable_if_t<std::is_same_v<T, std::string>, Ret>;
using is_string_t = std::enable_if_t<std::is_same_v<std::decay_t<T>, std::string>, Ret>;

template <typename T, typename Ret = T>
using is_c_string_t = std::enable_if_t<std::is_same_v<T, const char*>, Ret>;
using is_c_string_t = std::enable_if_t<std::is_same_v<std::decay_t<T>, const char*>, Ret>;

template <typename T, typename Ret = T>
using is_pointer_t = std::enable_if_t<std::is_pointer_v<T> && !std::is_same_v<T, const char*>, Ret>;
Expand Down Expand Up @@ -931,7 +931,6 @@ struct Stack {

/// Reports message to specified reporter and returns void.
/// \tparam Rs Return types.
/// \param reporter Reporter function.
/// \param message Message to report.
/// \return Void.
template <typename... Rs>
Expand All @@ -941,7 +940,6 @@ struct Stack {

/// Reports message to specified reporter and returns default constructed return type.
/// \tparam Rs Return types.
/// \param reporter Reporter function.
/// \param message Message to report.
/// \return Default constructed return type.
template <typename... Rs>
Expand All @@ -950,19 +948,16 @@ struct Stack {
return {};
}

/// Tries to get value from stack at specified index as C type. When failing returns default constructed type.
/// \tparam Rs Return types. Can be converted to tuple if multiple.
/// \param L Lua stack.
/// \param index Element index in stack.
/// \param reporter Error reporter function.
/// \return Value or default constructed type.
template <typename... Rs>
static decltype(auto) Get(lua_State* L, int index) {
try {
return Marshalling::GetValue<meta::multi_ret_t<Rs...>>(L, index);
} catch (const std::exception& e) {
return ReportErrorWithReturn<Rs...>(e.what());
}
/// Tries to get value/global from stack at specified index or with specific name. When failing returns default constructed type.
/// \tparam Rs Return type. When multiple, converts to tuple.
/// \tparam Keys Key type, either integral (index) or string (name).
/// \param L Lua state.
/// \param keys Keys to get values from.
/// \return Type or tuple of types with values.
template <typename... Rs, typename... Keys>
static decltype(auto) Get(lua_State* L, Keys&&... keys) {
static_assert(sizeof...(Rs) == sizeof...(Keys), "Number of returns and keys must match");
return meta::multi_ret_t<Rs...>(global<Rs>(L, std::forward<Keys>(keys))...);
}

/// Pushes values to Lua stack.
Expand Down Expand Up @@ -991,7 +986,6 @@ struct Stack {
/// \tparam Rs Void return specialization.
/// \tparam Args Arguments types.
/// \param L Lua stack.
/// \param reporter Reporter function.
/// \param args Arguments to call function with.
/// \return Void.
template <typename... Rs, typename... Args>
Expand All @@ -1008,7 +1002,6 @@ struct Stack {
/// \tparam Rs Return type of function.
/// \tparam Args Arguments types.
/// \param L Lua stack.
/// \param reporter Reporter function.
/// \param args Arguments to pass to Lua function.
/// \return Return value of Lua function.
template <typename... Rs, typename... Args>
Expand All @@ -1020,7 +1013,49 @@ struct Stack {
return ReportErrorWithReturn<Rs...>(e.what());
}
PopGuard guard{L, meta::count_expected_v<Rs...>};
return Get<Rs...>(L, -1);
return global<meta::multi_ret_t<Rs...>>(L, -1);
}

private:
/// Tries to get global with specified key name.
/// \tparam R Return type of values.
/// \tparam Key Global key type (string).
/// \param L Lua state.
/// \param key Global name.
/// \return C value.
template <typename R, typename Key>
static meta::is_string_t<Key, R> global(lua_State* L, Key&& key) {
lua_getglobal(L, key.c_str());
moon::Stack::PopGuard guard{L};
return global<R>(L, -1);
}

/// Tries to get global with specified key name.
/// \tparam R Return type of values.
/// \tparam Key Global key type (string).
/// \param L Lua state.
/// \param key Global name.
/// \return C value.
template <typename R, typename Key>
static meta::is_c_string_t<Key, R> global(lua_State* L, Key&& key) {
lua_getglobal(L, key);
moon::Stack::PopGuard guard{L};
return global<R>(L, -1);
}

/// Tries to get global at specified key index.
/// \tparam R Return type of values.
/// \tparam Key Global key type (integral).
/// \param L Lua state.
/// \param key Global index in stack.
/// \return C value.
template <typename R, typename Key>
static meta::is_integral_t<Key, R> global(lua_State* L, Key&& key) {
try {
return Marshalling::GetValue<R>(L, key);
} catch (const std::exception& e) {
return ReportErrorWithReturn<R>(e.what());
}
}
};

Expand Down Expand Up @@ -1354,24 +1389,14 @@ class Moon {
return Check<T>(GetTop());
}

/// Gets element at specified Lua stack index as C object.
/// \tparam R C type to cast Lua object to.
/// \param index Index of element in stack.
/// Gets element(s) at specified Lua stack index and/or global name as C object.
/// \tparam Rs C type(s) to cast Lua object to.
/// \tparam Keys Integral or string
/// \param keys Index or global name to get from Lua stack.
/// \return C object.
template <typename... R>
static inline decltype(auto) Get(int index = 1) {
return moon::Stack::Get<R...>(s_state, index);
}

/// Gets global Lua variable with specified name as C object.
/// \tparam R C object type.
/// \param name Lua variable name.
/// \return C object.
template <typename R>
static decltype(auto) Get(const std::string& name) {
lua_getglobal(s_state, name.c_str());
moon::Stack::PopGuard guard{s_state};
return Get<R>(GetTop());
template <typename... Rs, typename... Keys>
static inline decltype(auto) Get(Keys&&... keys) {
return moon::Stack::Get<Rs...>(s_state, std::forward<Keys>(keys)...);
}

/// Prints element at specified index. Shows value when possible or type otherwise.
Expand Down
67 changes: 67 additions & 0 deletions test/include/helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#ifndef STACK_GUARD_H
#define STACK_GUARD_H

#include "moon/moon.h"

#define BEGIN_STACK_GUARD \
int stack_top_begin{0}; \
int stack_top_end{0}; \
{ \
StackGuard guard{stack_top_begin, stack_top_end};

#define END_STACK_GUARD \
} \
REQUIRE(stack_top_begin == stack_top_end);

#define CHECK_STACK_GUARD REQUIRE(guard.Check());

class StackGuard {
public:
StackGuard(int& begin, int& end) : m_begin(begin), m_end(end) { m_begin = Moon::GetTop(); }

~StackGuard() { m_end = Moon::GetTop(); }

[[nodiscard]] inline bool Check() const { return Moon::GetTop() == m_begin; }

private:
int& m_begin;
int& m_end;
};

struct LoggerSetter {
LoggerSetter(std::string& info, std::string& warning, std::string& error) : m_info(info), m_warning(warning), m_error(error) {
m_info.clear();
m_warning.clear();
m_error.clear();
Moon::SetLogger([this](moon::Logger::Level level, const auto& msg) {
switch (level) {
case moon::Logger::Level::Info:
m_info = msg;
break;
case moon::Logger::Level::Warning:
m_warning = msg;
break;
case moon::Logger::Level::Error:
m_error = msg;
break;
}
});
}

inline const std::string& GetInfo() const { return m_info; }

inline const std::string& GetWarning() const { return m_warning; }

inline const std::string& GetError() const { return m_error; }

inline bool NoErrors() const { return m_error.empty(); }

private:
std::string& m_info;
std::string& m_warning;
std::string& m_error;
};

#endif
33 changes: 0 additions & 33 deletions test/include/stackguard.h

This file was deleted.

6 changes: 3 additions & 3 deletions test/include/userdefinedtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ class UserDefinedType {

explicit UserDefinedType(int prop) : m_prop(prop) {}

explicit UserDefinedType(lua_State*) : m_prop(Moon::Get<int>()) {}
explicit UserDefinedType(lua_State*) : m_prop(Moon::Get<int>(1)) {}

MOON_DECLARE_CLASS(UserDefinedType)

MOON_PROPERTY(m_prop)

MOON_METHOD(Getter) {
Moon::Push(m_prop + Moon::Get<int>());
Moon::Push(m_prop + Moon::Get<int>(1));
return 1;
}

MOON_METHOD(Setter) {
s_testClass = m_prop = Moon::Get<int>();
s_testClass = m_prop = Moon::Get<int>(1);
return 0;
}

Expand Down
10 changes: 5 additions & 5 deletions test/src/basic.test.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <catch2/catch.hpp>

#include "stackguard.h"
#include "helpers.h"

TEST_CASE("initialize and close state", "[basic]") {
Moon::Init();
Expand Down Expand Up @@ -305,7 +305,7 @@ TEST_CASE("push and get values, non-user types", "[basic]") {

SECTION("push and get a tuple") {
Moon::Push(std::make_tuple(1, true, "passed", std::make_tuple(2, false, "passed_2")));
auto t = Moon::Get<int, bool, std::string, int, bool, std::string>(-1);
auto t = Moon::Get<int, bool, std::string, int, bool, std::string>(1, 2, 3, 4, 5, 6);
REQUIRE(std::get<0>(t) == 1);
REQUIRE(std::get<1>(t));
REQUIRE(std::get<2>(t) == "passed");
Expand Down Expand Up @@ -490,21 +490,21 @@ SCENARIO("test multiple return getters", "[basic]") {
}

AND_THEN("values can be retrieved as tuple list") {
auto tup = Moon::Get<int, std::string, bool>(-1);
auto tup = Moon::Get<int, std::string, bool>(-3, -2, -1);
REQUIRE(std::get<0>(tup) == 1);
REQUIRE(std::get<1>(tup) == "passed");
REQUIRE(std::get<2>(tup));
}

AND_THEN("partial returns are still valid") {
REQUIRE(Moon::Get<bool>(-1));
auto tup = Moon::Get<std::string, bool>(-1);
auto tup = Moon::Get<std::string, bool>(-2, -1);
REQUIRE(std::get<0>(tup) == "passed");
REQUIRE(std::get<1>(tup));
}

AND_THEN("errors are still tracked in multi return") {
Moon::Get<std::string, bool, double>(-1);
Moon::Get<std::string, bool, double>(-3, -2, -1);
REQUIRE_FALSE(error.empty());
REQUIRE(error.find("type check failed") != error.size());
error.clear();
Expand Down
3 changes: 1 addition & 2 deletions test/src/functions.test.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#include <catch2/catch.hpp>

#include "moon/moon.h"
#include "stackguard.h"
#include "helpers.h"

TEST_CASE("call global lua functions", "[functions]") {
Moon::Init();
Expand Down
Loading

0 comments on commit 27161be

Please sign in to comment.