構造体ラッパー。オプションのパラメーターを構造体のフィールドとして定義します。
struct foo_args {
const char* title = "";
int year = 1900;
float percent = 0.0;
};
void foo(int a, int b, const foo_args& args = foo_args())
{
printf("title: %s\nyear: %d\npercent: %.2f\n",
args.title, args.year, args.percent);
}
int main()
{
foo_args args;
args.title = "foo title";
args.percent = 99.99;
foo(1, 2, args);
/* Note: in pure C brace initalizers could be used instead
but then you loose custom defaults -- non-initialized
fields are always zero.
foo_args args = { .title = "foo title", .percent = 99.99 };
*/
return 0;
}
プロキシオブジェクト。引数は、チェーンされたセッターで変更できる一時的な構造体に格納されます。
struct foo {
// Mandatory arguments
foo(int a, int b) : _a(a), _b(b) {}
// Optional arguments
// ('this' is returned for chaining)
foo& title(const char* title) { _title = title; return *this; }
foo& year(int year) { _year = year; return *this; }
foo& percent(float percent) { _percent = percent; return *this; }
// Do the actual call in the destructor.
// (can be replaced with an explicit call() member function
// if you're uneasy about doing the work in a destructor)
~foo()
{
printf("title: %s\nyear: %d\npercent: %.2f\n", _title, _year, _percent);
}
private:
int _a, _b;
const char* _title = "";
int _year = 1900;
float _percent = 0.0;
};
int main()
{
// Under the hood:
// 1. creates a proxy object
// 2. modifies it with chained setters
// 3. calls its destructor at the end of the statement
foo(1, 2).title("foo title").percent(99.99);
return 0;
}
注:読みやすさを犠牲にして、ボイラープレートをマクロに抽象化できます。
#define foo_optional_arg(type, name, default_value) \
public: foo& name(type name) { _##name = name; return *this; } \
private: type _##name = default_value
struct foo {
foo_optional_arg(const char*, title, "");
foo_optional_arg(int, year, 1900);
foo_optional_arg(float, percent, 0.0);
...
可変関数。これは明らかにタイプセーフではなく、正しくタイププロモーションを行うための知識が必要です。ただし、C ++がオプションでない場合は、純粋なCで使用できます。
#include <stdarg.h>
// Pre-defined argument tags
enum foo_arg { foo_title, foo_year, foo_percent, foo_end };
void foo_impl(int a, int b, ...)
{
const char* title = "";
int year = 1900;
float percent = 0.0;
va_list args;
va_start(args, b);
for (foo_arg arg = (foo_arg)va_arg(args, int); arg != foo_end;
arg = (foo_arg)va_arg(args, int))
{
switch(arg)
{
case foo_title: title = va_arg(args, const char*); break;
case foo_year: year = va_arg(args, int); break;
case foo_percent: percent = va_arg(args, double); break;
}
}
va_end(args);
printf("title: %s\nyear: %d\npercent: %.2f\n", title, year, percent);
}
// A helper macro not to forget the 'end' tag.
#define foo(a, b, ...) foo_impl((a), (b), ##__VA_ARGS__, foo_end)
int main()
{
foo(1, 2, foo_title, "foo title", foo_percent, 99.99);
return 0;
}
注:C ++では、可変個のテンプレートを使用してタイプセーフにすることができます。実行時間のオーバーヘッドは、コンパイル時間の低下とバイナリの膨張を犠牲にしてなくなります。
boost :: parameter。まだサードパーティのライブラリですが、一部のあいまいなgithubリポジトリよりもlibが確立されています。欠点:template-heavy。
#include <boost/parameter/name.hpp>
#include <boost/parameter/preprocessor.hpp>
#include <string>
BOOST_PARAMETER_NAME(foo)
BOOST_PARAMETER_NAME(bar)
BOOST_PARAMETER_NAME(baz)
BOOST_PARAMETER_NAME(bonk)
BOOST_PARAMETER_FUNCTION(
(int), // the return type of the function, the parentheses are required.
function_with_named_parameters, // the name of the function.
tag, // part of the deep magic. If you use BOOST_PARAMETER_NAME you need to put "tag" here.
(required // names and types of all required parameters, parentheses are required.
(foo, (int))
(bar, (float))
)
(optional // names, types, and default values of all optional parameters.
(baz, (bool) , false)
(bonk, (std::string), "default value")
)
)
{
if (baz && (bar > 1.0)) return foo;
return bonk.size();
}
int main()
{
function_with_named_parameters(1, 10.0);
function_with_named_parameters(7, _bar = 3.14);
function_with_named_parameters( _bar = 0.0, _foo = 42);
function_with_named_parameters( _bar = 2.5, _bonk= "Hello", _foo = 9);
function_with_named_parameters(9, 2.5, true, "Hello");
}
最後に、C ++には同じことを実現するのに十分な数の代替案があるため、このkwargsライブラリを使用しません。私は個人的に、上記の(完全ではない)リストから1.または2.を選択します。