#c #parameters #c 17
#c #параметры #c 17
Вопрос:
В JavaScript относительно часто встречаются объекты option, где все параметры имеют значения по умолчанию, и вы указываете только то, что вам нужно. Классическим примером является старая функция jQuery.ajax:
jQuery.ajax({
url: "https://google.com",
cache: true,
timeout: 500
// all other parameters are left to default values
});
Я знаю о назначенных инициализаторах C 20, функции, заимствованной из C99, которая решает эту проблему. Однако я использую компилятор C 17, поэтому я не могу их использовать.
Что я могу сделать?
Комментарии:
1. Вы можете использовать аргумент
struct
, который задает значения по умолчанию, и присваивать разные значения нужным полям, а затем передавать аргументstruct
тому, что использует эти аргументы.2. @Eljay Чтобы сделать это, мне пришлось бы объявить структуру, а затем передать структуру в функцию. Я ищу способ сделать это встроенным, как в приведенном примере.
3. Некоторые используют boost.org/doc/libs/1_74_0/libs/parameter/doc/html/index.html
Ответ №1:
Вы не можете получить такой точный синтаксис с помощью C 17, но вы могли бы приблизиться к этому, используя a std::map
для объединения аргументов в словарь. Также используется C 17 std::any
, чтобы значение могло быть любым подходящим.
#include <any>
#include <iostream>
#include <map>
#include <string>
using std::any;
using std::cout;
using std::map;
using std::string;
using std::string_literals::operator""s;
using dict_t = map<string, any>;
struct JQuery {
void ajax(dict_t constamp;);
};
void JQuery::ajax(dict_t constamp; dict) {
auto url_it = dict.find("url");
auto url = url_it != dict.end() ? std::any_cast<string>(url_it->second) : "default_url"s;
auto cache_it = dict.find("cache");
auto cache = cache_it != dict.end() ? std::any_cast<bool>(cache_it->second) : false;
auto timeout_it = dict.find("timeout");
auto timeout = timeout_it != dict.end() ? std::any_cast<int>(timeout_it->second) : 60;
cout << "url: " << url << "n";
cout << "cache: " << (cache ? "true" : "false") << "n";
cout << "timeout: " << timeout << "n";
}
int main() {
JQuery jQuery;
jQuery.ajax({
{ "url", "https://google.com"s },
{ "cache", true },
{ "timeout", 500 },
});
}
Комментарии:
1. Он действительно выполняет свою работу, но это довольно дорогой синтаксический сахар.
2. @rodrigocfd: Нет, это точно так же дорого, как версия JavaScript. Ну, JS может создать хеш-таблицу для выполнения поиска ключа / значения (вы могли бы использовать
unordered_map
), но в противном случае оба создают произвольное сопоставление ключей со значениями. Разница в том, что C заставляет вас произносить это по буквам; у него нет удобного синтаксиса для этого.
Ответ №2:
Ну, вы можете сделать что-то подобное, используя только проверки во время компиляции с C 17
#include <iostream>
#include <type_traits>
template<class Class>
struct custom_initializer {
using class_type = typename std::remove_cv_t<std::remove_reference_t<Class>>;
constexpr custom_initializer() = default;
template<auto Class::*MemberPtr, typename Type = decltype(std::declval<Class>().*MemberPtr)>
static class_type set(Typeamp;amp; value) noexcept {
static_assert(std::is_member_object_pointer_v<decltype(MemberPtr)>, "accept only pointers to members");
class_type res;
res.*MemberPtr = std::forward<Type>(value);
return res;
}
template<auto class_type::*FirstMember, auto... Members>
static class_type initialize(decltype(std::declval<class_type>().*FirstMember)amp;amp; first_value, decltype(std::declval<class_type>().*Members)amp;amp;... args) noexcept {
class_type res;
initialize_by_ref<FirstMember, Members...>(res, std::forward<decltype(std::declval<class_type>().*FirstMember)>(first_value), std::forward<decltype(std::declval<class_type>().*Members)>(args)...);
return res;
}
template<auto... Members>
static void initialize_by_ref(class_typeamp; object, decltype(std::declval<class_type>().*Members)amp;amp;... args) noexcept;
template<>
static void initialize_by_ref(class_typeamp; object) noexcept {}
template<auto class_type::*FirstMember, auto... Members>
static void initialize_by_ref(class_typeamp; object, decltype(std::declval<class_type>().*FirstMember)amp;amp; first_value, decltype(std::declval<class_type>().*Members)amp;amp;... args) noexcept {
static_assert(std::is_member_object_pointer_v<decltype(FirstMember)>, "accept only pointers to members");
object.*FirstMember = std::forward<decltype(std::declval<class_type>().*FirstMember)>(first_value);
initialize_by_ref<Members...>(object, std::forward<decltype(std::declval<class_type>().*Members)>(args)...);
}
};
struct Data {
char m_c1 = '0',
m_c2 = 'c',
m_c3 = '\';
bool m_b = false;
std::string m_str = "some text";
};
int main(int argc, char const *argv[]) {
auto data = custom_initializer<Data>::initialize<
amp;Data::m_b,
amp;Data::m_c2,
amp;Data::m_str
>(true, '4', "new value");
std::cout << data.m_b << std::endl;
std::cout << data.m_c2 << std::endl;
std::cout << data.m_str << std::endl;
return 0;
}
Обновить
Более «элегантное» решение:
#include <iostream>
#include <type_traits>
template<class Class>
struct initializer {
using class_type = typename std::remove_cv_t<std::remove_reference_t<Class>>;
static_assert(std::is_default_constructible_v<class_type>, "initializer template argument must be default constructible class (struct)");
initializer() = delete;
template<typename... Pairs>
static class_type create(Pairsamp;amp;... pairs) noexcept;
template<>
static class_type create() noexcept {
return class_type{};
}
template<typename Pair, typename... Pairs>
static class_type create(Pairamp;amp; pair, Pairsamp;amp;... pairs) noexcept {
class_type resu<
initialize(result, std::forward<Pair>(pair), std::forward<Pairs>(pairs)...);
return resu<
}
template<typename... Pairs>
static void initialize(class_typeamp; object, Pairsamp;amp;...) noexcept;
template<>
static void initialize(class_typeamp; object) noexcept {}
template<typename Pair, typename... Pairs>
static void initialize(class_typeamp; object, Pairamp;amp; pair, Pairsamp;amp;... pairs) noexcept {
using first_value_type = std::remove_reference_t<decltype(std::get<0>(std::declval<Pair>()))>;
using second_value_type = decltype(std::get<1>(std::declval<Pair>()));
static_assert(std::is_member_object_pointer_v<first_value_type>, "");
static_assert(std::is_convertible_v<std::remove_reference_t<second_value_type>, std::remove_reference_t<decltype(std::declval<class_type>().*std::declval<first_value_type>())>>, "");
object.*(std::get<0>(pair)) = std::forward<second_value_type>(std::get<1>(pair));
initialize(object, std::forward<Pairs>(pairs)...);
}
};
struct JQuery {
template<class Object, typename... Args1, typename... Args2>
static void ajax(const std::pair<Args1, Args2>amp;... pairs) noexcept {
ajax(initializer<Object>::create(pairs...));
}
template<class Object>
static void ajax(Objectamp;amp; object) noexcept {
// ... do smth
std::cout << "initialization completed" << std::endl;
}
};
struct Data {
char m_c1 = '0',
m_c2 = 'c',
m_c3 = '\';
bool m_b = false;
std::string m_str = "some text";
};
int main(int argc, char const *argv[]) {
char c2 = 'w';
Data data = initializer<Data>::create(
std::pair {amp;Data::m_b, true},
std::pair {amp;Data::m_str, "new text"},
std::pair {amp;Data::m_c2, c2}
);
std::cout << data.m_b << std::endl;
std::cout << data.m_str << std::endl;
std::cout << data.m_c2 << std::endl;
// same but with "JQuery" mock
JQuery::ajax<Data>(
std::pair {amp;Data::m_b, true},
std::pair {amp;Data::m_str, "new text"},
std::pair {amp;Data::m_c2, c2}
);
return 0;
}