satoren / kaguya

C++ binding to Lua
Boost Software License 1.0
345 stars 70 forks source link

Support default parameters #17

Closed Flamefire closed 8 years ago

Flamefire commented 8 years ago

Is it somehow possible, to support default parameters without writing extra functions? Like:

class Foo{
  void func(int param = 42){ std::cout << param; }
};

Currently those functions can be registered, but if the param is notpassed by Lua , it will be 0 instead of 42

satoren commented 8 years ago

I don't know about detect function have default parameter.

other way is use overload.

struct Foo {
    void func(int param = 42) { std::cout << param<<std::endl; }
};
state["Foo"].setClass(kaguya::ClassMetatable<Foo>()
    .addMemberFunction("func", &Foo::func)
    .addStaticFunction("func", [](Foo& self) {self.func(); })
);
state["foo"] = Foo();
state("foo:func()");//42
state("foo:func(1)");//1
);
ynlh commented 8 years ago

When can support default parameters?

satoren commented 8 years ago

When i hit upon a good idea.

satoren commented 8 years ago

Added support for Default parameters


/**
* @name KAGUYA_FUNCTION_OVERLOADS
* @brief Generate wrapper function object for count based overloads. Include default arguments parameter function
* @param GENERATE_NAME generate function object name
* @param FNAME target function name
* @param MINARG minimum arguments count
* @param MAXARG maximum arguments count
*/
#define KAGUYA_FUNCTION_OVERLOADS(GENERATE_NAME,FNAME, MINARG, MAXARG) KAGUYA_FUNCTION_OVERLOADS_INTERNAL(GENERATE_NAME,FNAME, MINARG, MAXARG)

/**
* @name KAGUYA_FUNCTION_OVERLOADS
* @brief Generate wrapper function object for count based overloads. Include default arguments parameter function
* @param GENERATE_NAME generate function object name
* @param CLASS target class name
* @param FNAME target function name
* @param MINARG minimum arguments count
* @param MAXARG maximum arguments count
*/
#define KAGUYA_MEMBER_FUNCTION_OVERLOADS(GENERATE_NAME,CLASS,FNAME, MINARG, MAXARG) KAGUYA_MEMBER_FUNCTION_OVERLOADS_INTERNAL(GENERATE_NAME,CLASS,FNAME, MINARG, MAXARG)

example

//free function
int defargfn(int a = 3, int b = 2, int c = 1)
{
    return a*b*c;
}

KAGUYA_FUNCTION_OVERLOADS(defargfn_wrapper, defargfn,0,3)
state["defarg"] = kaguya::function(defargfn_wrapper);
state.dostring("assert(defarg() == 6)");
state.dostring("assert(defarg(6) == 12)");
state.dostring("assert(defarg(6,5) == 30)");
state.dostring("assert(defarg(2,2,2) == 8)");

//member function
struct TestClass
{
  int defargfn(int a = 3, int b = 2, int c = 1)
  {
      return a*b*c;
  }
}
KAGUYA_MEMBER_FUNCTION_OVERLOADS(defargfn_wrapper, TestClass, default_arg, 0, 3)
state["TestClass"].setClass(kaguya::UserdataMetatable<TestClass>()
  .setConstructors<TestClass()>()
  .addFunction("defarg", defargfn_wrapper)
);
    state.dostring("test = TestClass.new()");
    state.dostring("assert(test:defargfn() == 6)");
    state.dostring("assert(test:defargfn(6) == 12)");
    state.dostring("assert(test:defargfn(6,5) == 30)");
    state.dostring("assert(test:defargfn(2,2,2) == 8)");
aster2013 commented 8 years ago

Build error on VS2015 when function return void.

'kaguya::util::push_args': function does not take 2 arguments
satoren commented 8 years ago

Add void return version , because can not fix on quick. I put together they, If it can created.

aster2013 commented 8 years ago

When functions with different signatures, these macros can not work. for example.

struct Test
{
    void add(int a, int b = 0);
    void add(const std::string& a, const std::string& b = "");
};
aster2013 commented 8 years ago

Here is my current solution:

static void addInt(Test* self, int a, int b = 0)
{
    self->add(a, b);
}
static void addString(Test* self, const std::string& a, const std::string& b = "")
{
    self->add(a, b);
}

...

KAGUYA_VOID_FUNCTION_OVERLOADS(addInt, addInt, 1, 2);
KAGUYA_VOID_FUNCTION_OVERLOADS(addString,  addString, 1, 2);

...

.addOverloadedFunctions("add", addInt, addString)
aster2013 commented 8 years ago

Hi, where you pushed, not on the master?

satoren commented 8 years ago

I'm waited test pass on my CI.

Usage was little changed.

example


KAGUYA_FUNCTION_OVERLOADS(defargfn_wrapper, defargfn, 0, 3);
KAGUYA_FUNCTION_OVERLOADS_WITH_SIGNATURE(defargfn_wrapper_with_sig, defargfn, 0, 3,int(int,int,int));
KAGUYA_MEMBER_FUNCTION_OVERLOADS_WITH_SIGNATURE(mem_defargfn_wrapper_with_sig, TestClass, default_arg, 0, 3,int(TestClass::*)(int,int,int));

...

state["defargfn"] = kaguya::function(defargfn_wrapper());
state["defargfn2"] = kaguya::function(defargfn_wrapper_with_sig());

state["TestClass"].setClass(kaguya::UserdataMetatable<TestClass>()
    .addFunction("defargfn", mem_defargfn_wrapper_with_sig())
);
aster2013 commented 8 years ago

Bug:

For example, a overloaded function with following arguments:

void Test(const std::string& a = "hello");

We need a const reference as input, but the lua_type_traits<std::string>::get(...) returns std::string.

So bug in following code:

namespace nativefunction
{
    template<size_t INDEX,typename F>
    typename util::ArgumentType<INDEX, F>::type getArgument(lua_State* state)
    {
        return lua_type_traits<typename util::ArgumentType<INDEX, F>::type>::get(state, INDEX + 1);
    }

    // explanded:

    template<size_t INDEX,typename F>
    const std::string& getArgument(lua_State* state)
    {
        std::string value = lua_type_traits<std::string>::get(state, INDEX + 1);
        return value; // <--- Error, return a temporary variable.
    }
  }
satoren commented 8 years ago

Thank you for report.

aster2013 commented 8 years ago

And another bug: When some function with const char* argument, kaguya will report error:

cannot convert argument 2 from 'kaguya::lua_type_traits<const char*,void>::get_type' to 'const char *'
aster2013 commented 8 years ago
template<>  struct lua_type_traits<const char*> {
        // typedef std::string get_type;
        typedef const char* get_type;
        typedef const char* push_type;
satoren commented 8 years ago

Thanks!