Информация об изменениях

Сообщение Re[10]: Философско-практические вопросы про метапрограммиров от 11.02.2023 15:49

Изменено 11.02.2023 17:03 rg45

Re[10]: Философско-практические вопросы про метапрограммиров
Здравствуйте, ботаныч, Вы писали:

Б>https://onlinegdb.com/4hmabsGci

Б>да вот же, я же говорю, это просто проверка на инстанс сета ))

Зачем нам эта проверка? Эта утилита же в контексте задачи родилась, правда? Вот и примени это при решении задачи: http://coliru.stacked-crooked.com/a/fdbaf2f292d21344. Только не умозрительно, а чтоб работало. Потом сравним. Обрати внимание, пример немного изменен — добавлены пространства имен, как это обычно бывает в реальной жизни.

P.S. Единственный плюс такого подхода тот, что позволит перечислить все стандартные контейнеры в одной специализации. Но плюс достаточно сомнительный, как по мне, если учесть, что этой для того, чтобы эта монолитная специализация стала возможна, сперва нужно определить несколько дополнительных сущщностей, не слишком удобочитаемых. А в случаях, когда предполагается расширение специализаций для пользовательских типов, разбросанных по разным пространствам имен, использование этого подхода крайне неудобно и порождает в местах специализаций нагромождения кода, которые ужасно будут раздражать при первом же рефакторинге. Нагромождения связаны в первую очередь необходимостью разрывать пользовательские пространства имен для выполнения специализаций библиотечного класса. И, чем больше таких мест, тем более ужасная картина будет получаться. Вынос этих специализаций в какое-то отдельное место также имее свои ощутимые недостатки.

Вот видоизмененный пример, на котором можно прочувствовать проблему, о которой я говорю. Центральное место здесь занимает пользовательский класс MySortedContainer, который должен обрабатыватся по тем же правилам, что и отсортированные стандартные контейнеры (map, set и пр.).

http://coliru.stacked-crooked.com/a/fdbaf2f292d21344.

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <utility>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic Library Area

namespace lib
{

template <typename, typename = void>
struct has_sort_member_function : std::false_type {};

template <typename T>
struct has_sort_member_function<T, std::void_t<decltype(std::declval<T&>().sort())>> : std::true_type {};

template <typename, typename = void>
struct is_std_sort_applicable : std::false_type{};

template <typename T>
struct is_std_sort_applicable<T, std::void_t<
   decltype(std::sort(std::begin(std::declval<T&>()), std::end(std::declval<T&>()))),
   decltype(*std::begin(std::declval<T&>()) = *std::begin(std::declval<T&>()))
>> : std::true_type {};

template <typename T>
auto assume_sorted_range(const T&) -> std::false_type;

// For standard containers, the traits must be defined beforehand
template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::map<K, T, C, A>&) -> std::true_type;

template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::multimap<K, T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::set<T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::multiset<T, C, A>&) -> std::true_type;

template <typename T>
using is_sorted_container = decltype(assume_sorted_range(std::declval<T>()));

template <typename T, std::enable_if_t<has_sort_member_function<T>::value, int> = 0>
void sort(T& t)
{
   t.sort();
}

template <typename T, std::enable_if_t<
   is_std_sort_applicable<T>::value &&
   !has_sort_member_function<T>::value &&
   !is_sorted_container<T>::value,
int> = 0>
void sort(T& t)
{
   std::sort(std::begin(t), std::end(t));
}

template <typename T, std::enable_if_t<
   is_sorted_container<T>::value &&
   !has_sort_member_function<T>::value,
int> = 0>
void sort(const T&)
{
   // Container is sorted already, so nothing to do.
}

} // namespace lib


namespace app1 {
namespace app2 {
namespace app3 {    
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Application Area

class MySortedContainer
{
public:

    auto begin() const { return m.begin(); }
    auto end() const { return m.end(); }

    void add_element(int key, int value) { m.emplace(key, value); }

private:
    std::map<int, int> m;
};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

struct MyContainer
{
   void sort() { std::cout << "MyContainer::sort" << std::endl; }
};

void test()
{
   std::string string = "Hello World!";
   lib::sort(string);
   std::cout << "<" << string << ">" << std::endl;

   int array[] { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(array);

   std::vector<int> vector { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(vector);

   std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(set); // no effect

   MyContainer my_container;
   lib::sort(my_container);

   MySortedContainer my_sorted_container;
   lib::sort(my_sorted_container); // no effect

   char str[] = "Hello World!";
   lib::sort(str); // Ok
   // lib::sort("Hello World!"); // error: no matching function for call
}

} // namespace app3
} // namespace app2
} // namespace app1

int main()
{
    app1::app2::app3::test();
}
Re[10]: Философско-практические вопросы про метапрограммиров
Здравствуйте, ботаныч, Вы писали:

Б>https://onlinegdb.com/4hmabsGci

Б>да вот же, я же говорю, это просто проверка на инстанс сета ))

А для чего нам это проверка нужна вообще? Эта утилита родилась же в контексте задачи, правда? Вот и попробуй применить свой подход при решении задачи: http://coliru.stacked-crooked.com/a/fdbaf2f292d21344. Только не умозрительно, а чтоб работало. Потом сравним. Обрати внимание, пример немного изменен — добавлены пространства имен, как это обычно бывает в реальной жизни.

P.S. Единственный плюс такого подхода тот, что позволит перечислить все стандартные контейнеры в одной специализации. Но плюс достаточно сомнительный, как по мне, если учесть, что этой для того, чтобы эта монолитная специализация стала возможна, сперва нужно определить несколько дополнительных сущщностей, не слишком удобочитаемых. А в случаях, когда предполагается расширение специализаций для пользовательских типов, разбросанных по разным пространствам имен, использование этого подхода крайне неудобно и порождает в местах специализаций нагромождения кода, которые ужасно будут раздражать при первом же рефакторинге. Нагромождения связаны в первую очередь необходимостью разрывать пользовательские пространства имен для выполнения специализаций библиотечного класса. И, чем больше таких мест, тем более ужасная картина будет получаться. Вынос этих специализаций в какое-то отдельное место также имее свои ощутимые недостатки.

Вот видоизмененный пример, на котором можно прочувствовать проблему, о которой я говорю. Центральное место здесь занимает пользовательский класс MySortedContainer, который должен обрабатыватся по тем же правилам, что и отсортированные стандартные контейнеры (map, set и пр.).

http://coliru.stacked-crooked.com/a/fdbaf2f292d21344.

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include <utility>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Generic Library Area

namespace lib
{

template <typename, typename = void>
struct has_sort_member_function : std::false_type {};

template <typename T>
struct has_sort_member_function<T, std::void_t<decltype(std::declval<T&>().sort())>> : std::true_type {};

template <typename, typename = void>
struct is_std_sort_applicable : std::false_type{};

template <typename T>
struct is_std_sort_applicable<T, std::void_t<
   decltype(std::sort(std::begin(std::declval<T&>()), std::end(std::declval<T&>()))),
   decltype(*std::begin(std::declval<T&>()) = *std::begin(std::declval<T&>()))
>> : std::true_type {};

template <typename T>
auto assume_sorted_range(const T&) -> std::false_type;

// For standard containers, the traits must be defined beforehand
template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::map<K, T, C, A>&) -> std::true_type;

template <typename K, typename T, typename C, typename A>
auto assume_sorted_range(const std::multimap<K, T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::set<T, C, A>&) -> std::true_type;

template <typename T, typename C, typename A>
auto assume_sorted_range(const std::multiset<T, C, A>&) -> std::true_type;

template <typename T>
using is_sorted_container = decltype(assume_sorted_range(std::declval<T>()));

template <typename T, std::enable_if_t<has_sort_member_function<T>::value, int> = 0>
void sort(T& t)
{
   t.sort();
}

template <typename T, std::enable_if_t<
   is_std_sort_applicable<T>::value &&
   !has_sort_member_function<T>::value &&
   !is_sorted_container<T>::value,
int> = 0>
void sort(T& t)
{
   std::sort(std::begin(t), std::end(t));
}

template <typename T, std::enable_if_t<
   is_sorted_container<T>::value &&
   !has_sort_member_function<T>::value,
int> = 0>
void sort(const T&)
{
   // Container is sorted already, so nothing to do.
}

} // namespace lib


namespace app1 {
namespace app2 {
namespace app3 {    
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Custom Application Area

class MySortedContainer
{
public:

    auto begin() const { return m.begin(); }
    auto end() const { return m.end(); }

    void add_element(int key, int value) { m.emplace(key, value); }

private:
    std::map<int, int> m;
};
auto assume_sorted_range(const MySortedContainer&) -> std::true_type;

struct MyContainer
{
   void sort() { std::cout << "MyContainer::sort" << std::endl; }
};

void test()
{
   std::string string = "Hello World!";
   lib::sort(string);
   std::cout << "<" << string << ">" << std::endl;

   int array[] { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(array);

   std::vector<int> vector { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(vector);

   std::set<int> set { 3, 5, 2, 9, 1, 7, 4, 8, 0, 6 };
   lib::sort(set); // no effect

   MyContainer my_container;
   lib::sort(my_container);

   MySortedContainer my_sorted_container;
   lib::sort(my_sorted_container); // no effect

   char str[] = "Hello World!";
   lib::sort(str); // Ok
   // lib::sort("Hello World!"); // error: no matching function for call
}

} // namespace app3
} // namespace app2
} // namespace app1

int main()
{
    app1::app2::app3::test();
}