Skip to content

Commit 5f0f3cf

Browse files
committed
Revamp handling of ruby arguments - use two passes instead of one. The first pass extracts passed in Ruby args and the second fills in default values per different native functions/methods.
1 parent b2b67a4 commit 5f0f3cf

25 files changed

+153
-102
lines changed

rice/Arg.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ namespace Rice
8383

8484
public:
8585
std::string name;
86-
int32_t position = -1;
8786

8887
private:
8988
//! Our saved default value

rice/detail/InstanceRegistry.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#ifndef Rice__detail__InstanceRegistry__hpp_
22
#define Rice__detail__InstanceRegistry__hpp_
33

4-
#include <map>
5-
64
namespace Rice::detail
75
{
86
class InstanceRegistry

rice/detail/Native.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ namespace Rice::detail
4242
void operator=(const Native&) = delete;
4343
void operator=(Native&&) = delete;
4444

45-
virtual Resolved matches(size_t argc, const VALUE* argv);
46-
virtual VALUE operator()(size_t argc, const VALUE* argv, VALUE self) = 0;
45+
virtual Resolved matches(std::map<std::string, VALUE>& values);
46+
virtual VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) = 0;
4747
virtual std::string toString() = 0;
4848

4949
// Ruby API access
@@ -56,7 +56,8 @@ namespace Rice::detail
5656
template<typename T, bool isBuffer>
5757
static void verify_type();
5858

59-
std::vector<std::optional<VALUE>> getRubyValues(size_t argc, const VALUE* argv, bool validate);
59+
static std::map<std::string, VALUE> readRubyArgs(size_t argc, const VALUE* argv);
60+
std::vector<std::optional<VALUE>> getRubyValues(std::map<std::string, VALUE> values, bool validate);
6061
ParameterAbstract* getParameterByName(std::string name);
6162
Convertible matchParameters(std::vector<std::optional<VALUE>>& values);
6263

rice/detail/Native.ipp

Lines changed: 85 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ namespace Rice::detail
6565
// Execute the function but make sure to catch any C++ exceptions!
6666
return cpp_protect([&]()
6767
{
68+
std::map<std::string, VALUE> values = readRubyArgs(argc, argv);
69+
6870
const std::vector<std::unique_ptr<Native>>& natives = Registries::instance.natives.lookup(klass, methodId);
6971

7072
if (natives.size() == 1)
@@ -86,7 +88,7 @@ namespace Rice::detail
8688
std::back_inserter(resolves),
8789
[&](const std::unique_ptr<Native>& native)
8890
{
89-
return native->matches(argc, argv);
91+
return native->matches(values);
9092
});
9193

9294
// Now sort from best to worst
@@ -146,7 +148,7 @@ namespace Rice::detail
146148
}
147149

148150
// Call the C++ function
149-
return (*native)(argc, argv, self);
151+
return (*native)(values, self);
150152
});
151153
}
152154

@@ -290,14 +292,8 @@ namespace Rice::detail
290292
// Fill in missing args
291293
for (size_t i = argsVector.size(); i < std::tuple_size_v<Parameter_Tuple>; i++)
292294
{
293-
argsVector.emplace_back(std::make_unique<Arg>("arg_" + std::to_string(i)));
294-
}
295-
296-
// TODO - there has to be a better way!
297-
for (size_t i = 0; i < argsVector.size(); i++)
298-
{
299-
std::unique_ptr<Arg>& arg = argsVector[i];
300-
arg->position = (int32_t)i;
295+
std::string argName = "arg_" + std::to_string(i);
296+
argsVector.emplace_back(std::make_unique<Arg>(argName));
301297
}
302298

303299
auto indices = std::make_index_sequence<std::tuple_size_v<Parameter_Tuple>>{};
@@ -328,81 +324,116 @@ namespace Rice::detail
328324
return result;
329325
}
330326

331-
inline std::vector<std::optional<VALUE>> Native::getRubyValues(size_t argc, const VALUE* argv, bool validate)
327+
inline std::map<std::string, VALUE> Native::readRubyArgs(size_t argc, const VALUE* argv)
332328
{
333-
#undef max
334-
size_t size = std::max(this->parameters_.size(), argc);
335-
std::vector<std::optional<VALUE>> result(size);
329+
std::map<std::string, VALUE> result;
336330

337331
// Keyword handling
338332
if (rb_keyword_given_p())
339333
{
340334
// Keywords are stored in the last element in a hash
341335
size_t actualArgc = argc - 1;
342336

343-
VALUE value = argv[actualArgc];
344-
Hash keywords(value);
345-
346337
// Copy over leading non-keyword arguments
347338
for (size_t i = 0; i < actualArgc; i++)
348339
{
349-
result[i] = argv[i];
340+
std::string key = "arg_" + std::to_string(i);
341+
result[key] = argv[i];
350342
}
351343

344+
VALUE value = argv[actualArgc];
345+
Hash keywords(value);
346+
352347
// Copy over keyword arguments
353348
for (auto pair : keywords)
354349
{
355-
Symbol key(pair.first);
356-
ParameterAbstract* parameter = this->getParameterByName(key.str());
357-
if (!parameter && validate)
358-
{
359-
throw std::invalid_argument("Unknown keyword: " + key.str());
360-
}
361-
else if (!parameter)
362-
{
363-
continue;
364-
}
365-
366-
const Arg* arg = parameter->arg();
367-
368-
result[arg->position] = pair.second.value();
350+
result[pair.first.to_s().str()] = pair.second.value();
369351
}
370352
}
371353
else
372354
{
373-
std::copy(argv, argv + argc, result.begin());
355+
// Copy over leading non-keyword arguments
356+
for (size_t i = 0; i < argc; i++)
357+
{
358+
std::string key = "arg_" + std::to_string(i);
359+
result[key] = argv[i];
360+
}
374361
}
375362

376363
// Block handling. If we find a block and the last parameter is missing then
377364
// set it to the block
378-
if (rb_block_given_p() && result.size() > 0 && !result.back().has_value())
365+
if (rb_block_given_p())// FIXME && result.size() > 0 && !result.back().second.has_value())
379366
{
380367
VALUE proc = rb_block_proc();
381-
result.back() = proc;
368+
std::string key = "arg_" + std::to_string(result.size());
369+
result[key] = proc;
382370
}
383371

384-
if (validate)
372+
return result;
373+
}
374+
375+
inline std::vector<std::optional<VALUE>> Native::getRubyValues(std::map<std::string, VALUE> values, bool validate)
376+
{
377+
// !!!NOTE!!! We copied the values parameter because we are going to modify it!
378+
379+
// Protect against user sending too many arguments
380+
if (values.size() > this->parameters_.size())
381+
{
382+
std::string message = "wrong number of arguments (given " +
383+
std::to_string(values.size()) + ", expected " + std::to_string(this->parameters_.size()) + ")";
384+
throw std::invalid_argument(message);
385+
}
386+
387+
std::vector<std::optional<VALUE>> result(this->parameters_.size());
388+
389+
for (size_t i=0; i< this->parameters_.size(); i++)
385390
{
386-
// Protect against user sending too many arguments
387-
if (argc > this->parameters_.size())
391+
std::unique_ptr<ParameterAbstract>& parameter = this->parameters_[i];
392+
Arg* arg = parameter->arg();
393+
394+
// If using keywords arguments, then the value key will be arg->name(). If using positional
395+
// arguments then they key will be "arg_<position>"
396+
std::string keywordKey = arg->name;
397+
std::string positionKey = "arg_" + std::to_string(i);
398+
399+
auto iter = values.find(keywordKey);
400+
if (iter == values.end() && keywordKey != positionKey)
388401
{
389-
std::string message = "wrong number of arguments (given " +
390-
std::to_string(argc) + ", expected " + std::to_string(this->parameters_.size()) + ")";
391-
throw std::invalid_argument(message);
402+
iter = values.find(positionKey);
392403
}
393404

394-
for (size_t i = 0; i < result.size(); i++)
405+
if (iter != values.end())
406+
{
407+
result[i] = iter->second;
408+
// Remove the value
409+
values.erase(iter);
410+
}
411+
else if (arg->hasDefaultValue())
412+
{
413+
result[i] = parameter->defaultValueRuby();
414+
}
415+
else if (validate)
395416
{
396-
std::optional<VALUE> value = result[i];
397-
ParameterAbstract* parameter = this->parameters_[i].get();
417+
std::string message = "Missing argument. Name: " + arg->name + ". Index: " + std::to_string(i) + ".";
418+
throw std::invalid_argument(message);
419+
}
420+
}
398421

399-
if (!parameter->arg()->hasDefaultValue() && !value.has_value())
400-
{
401-
std::string message;
402-
message = "Missing argument. Name: " + parameter->arg()->name + ". Index: " + std::to_string(parameter->arg()->position) + ".";
403-
throw std::invalid_argument(message);
404-
}
422+
// Check for unknown arguments
423+
if (validate && values.size() > 0)
424+
{
425+
// There are unknown arguments
426+
std::ostringstream message;
427+
message << "Unknown argument(s): ";
428+
size_t count = 0;
429+
for (const std::pair<const std::string, VALUE>& pair : values)
430+
{
431+
if (count > 0)
432+
message << ", ";
433+
message << pair.first;
434+
count++;
405435
}
436+
throw std::invalid_argument(message.str());
406437
}
407438

408439
return result;
@@ -420,25 +451,20 @@ namespace Rice::detail
420451
return result;
421452
}
422453

423-
inline Resolved Native::matches(size_t argc, const VALUE* argv)
454+
inline Resolved Native::matches(std::map<std::string, VALUE>& values)
424455
{
425456
// Return false if Ruby provided more arguments than the C++ method takes
426-
if (argc > this->parameters_.size())
457+
if (values.size() > this->parameters_.size())
427458
return Resolved{ Convertible::None, 0, this };
428459

429460
Resolved result{ Convertible::Exact, 1, this };
430461

431-
std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(argc, argv, false);
462+
std::vector<std::optional<VALUE>> rubyValues = this->getRubyValues(values, false);
432463
result.convertible = this->matchParameters(rubyValues);
433464

434465
if (this->parameters_.size() > 0)
435466
{
436-
size_t providedValues = std::count_if(rubyValues.begin(), rubyValues.end(), [](std::optional<VALUE>& value)
437-
{
438-
return value.has_value();
439-
});
440-
441-
result.parameterMatch = providedValues / (double)this->parameters_.size();
467+
result.parameterMatch = values.size() / (double)this->parameters_.size();
442468
}
443469
return result;
444470
}

rice/detail/NativeAttributeGet.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ namespace Rice
3535
void operator=(const NativeAttribute_T&) = delete;
3636
void operator=(NativeAttribute_T&&) = delete;
3737

38-
Resolved matches(size_t argc, const VALUE* argv) override;
39-
VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
38+
Resolved matches(std::map<std::string, VALUE>& values) override;
39+
VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) override;
4040
std::string toString() override;
4141

4242
NativeKind kind() override;

rice/detail/NativeAttributeGet.ipp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ namespace Rice::detail
2727
}
2828

2929
template<typename Attribute_T>
30-
inline Resolved NativeAttributeGet<Attribute_T>::matches(size_t argc, const VALUE*)
30+
inline Resolved NativeAttributeGet<Attribute_T>::matches(std::map<std::string, VALUE>& values)
3131
{
32-
if (argc == 0)
32+
if (values.size() == 0)
3333
return Resolved { Convertible::Exact, 1, this };
3434
else
3535
return Resolved{ Convertible::None, 0, this };
@@ -43,7 +43,7 @@ namespace Rice::detail
4343
}
4444

4545
template<typename Attribute_T>
46-
inline VALUE NativeAttributeGet<Attribute_T>::operator()(size_t, const VALUE*, VALUE self)
46+
inline VALUE NativeAttributeGet<Attribute_T>::operator()(std::map<std::string, VALUE>&, VALUE self)
4747
{
4848
if constexpr (std::is_member_object_pointer_v<Attribute_T>)
4949
{

rice/detail/NativeAttributeSet.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace Rice
2626
void operator=(const NativeAttribute_T&) = delete;
2727
void operator=(NativeAttribute_T&&) = delete;
2828

29-
Resolved matches(size_t argc, const VALUE* argv) override;
30-
VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
29+
Resolved matches(std::map<std::string, VALUE>& values) override;
30+
VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) override;
3131
std::string toString() override;
3232

3333
NativeKind kind() override;

rice/detail/NativeAttributeSet.ipp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@ namespace Rice::detail
3131
}
3232

3333
template<typename Attribute_T>
34-
inline Resolved NativeAttributeSet<Attribute_T>::matches(size_t argc, const VALUE*)
34+
inline Resolved NativeAttributeSet<Attribute_T>::matches(std::map<std::string, VALUE>& values)
3535
{
36-
if (argc == 1)
36+
if (values.size() == 1)
3737
return Resolved{ Convertible::Exact, 1, this };
3838
else
3939
return Resolved{ Convertible::None, 0, this };
4040
}
4141

4242
template<typename Attribute_T>
43-
inline VALUE NativeAttributeSet<Attribute_T>::operator()(size_t argc, const VALUE* argv, VALUE self)
43+
inline VALUE NativeAttributeSet<Attribute_T>::operator()(std::map<std::string, VALUE>& values, VALUE self)
4444
{
45-
if (argc != 1)
45+
if (values.size() != 1)
4646
{
4747
throw std::runtime_error("Incorrect number of parameters for setting attribute. Attribute: " + this->name_);
4848
}
4949

50-
VALUE value = argv[0];
50+
VALUE value = values.begin()->second;
5151

5252
if constexpr (!std::is_null_pointer_v<Receiver_T>)
5353
{

rice/detail/NativeCallback.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ namespace Rice::detail
5151
static inline NativeCallback_T* native_;
5252

5353
private:
54-
VALUE operator()(size_t argc, const VALUE* argv, VALUE self) override;
54+
VALUE operator()(std::map<std::string, VALUE>& values, VALUE self) override;
5555
std::string toString() override;
5656
NativeKind kind() override;
5757
VALUE returnKlass() override;

rice/detail/NativeCallback.ipp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ namespace Rice::detail
243243
}
244244

245245
template<typename Return_T, typename ...Parameter_Ts>
246-
inline VALUE NativeCallback<Return_T(*)(Parameter_Ts...)>::operator()(size_t, const VALUE*, VALUE)
246+
inline VALUE NativeCallback<Return_T(*)(Parameter_Ts...)>::operator()(std::map<std::string, VALUE>&, VALUE)
247247
{
248248
return Qnil;
249249
}

0 commit comments

Comments
 (0)