#include <stdio.h> 

/**
 * Это собственно реализация именованных параметров, пропускаем
 */
#define structer(name, param) typedef struct param name;
#define fname(f) f ## _func
#define sname(f) f ## _stru
#define function_(type, f, argname, param)\
	structer(sname(f), param) type fname(f)(sname(f) argname)
#define bind(f, ...) fname(f)((sname(f)) {__VA_ARGS__})


/**
 * Синтаксис
 * function_(<return_type>, <function_name>, <arg_container_name>, {<arguments>})
 * Объявляешь как обычную функцию, только теперь параметры в её теле
 * попадут в структуру <arg_container_name>
 * 
 * Пример ниже: длина вектора.
 * Выглядит стрёмно, но зато потом можно, во-первых, забиндить параметры по умолчанию,
 * а во-вторых, использовать с именованными аргументами (как в питоне)
 */
function_(double, vectorLength, in, {double x; double y; double z;}) {
	return sqrt(in.x*in.x + in.y*in.y + in.z*in.z);
}

/**
 * Синтаксис
 * #define <new_f_name>(...) bind(<function_name>, <default_values...>, __VA_ARGS__)
 * <function_name> должна быть фукнцией, объявленной способом выше (через function_).
 * <new_f_name> это уже реально используемая фукнция, только параметры ей передаются
 * префиксированные точкой (как записаны <default_values> в примере ниже).
 * В конце нужно обязательно дописывать __VA_ARGS__
 * 
 * Пример: взяли vectorLength и переделали в vectorLengthZero, у которого
 * аргументы по умолчанию равны нулю.
 * Второй пример: аргументы по умолчанию делаем равными 3, 0 и 0.
 */
#define vectorLengthZero(...) bind(vectorLength, .x=0, .y=0, .z=0,  __VA_ARGS__)
#define vectorLengthOne(...)  bind(vectorLength, .x=3, .y=0, .z=0,  __VA_ARGS__)

int main() { 
    printf("|(0,0,0)|: %f\n", vectorLengthZero() );
    printf("|(1,0,0)|: %f\n", vectorLengthZero(.x = 1) );
    printf("|(0,1,0)|: %f\n", vectorLengthZero(.y = 1) );
    printf("|(1,1,0)|: %f\n", vectorLengthZero(.y = 1, .x = 1) );
    printf("\n");
    printf("|(3,0,0)|: %f\n", vectorLengthOne() );
    printf("|(3,0,4)|: %f\n", vectorLengthOne(.z = 4) );
    printf("|(12,3,4)|: %f\n", vectorLengthOne(.z = 4, .y = 3, .x = 12) );
    return 0;
}