Почему люди часто ошибаются при написании простых функций сравнения?
От: Analytic2007 Россия https://www.viva64.com/ru/pvs-studio/
Дата: 21.04.16 13:27
Оценка: 22 (1) -1
Я регулярно пишу статьи о программировании и выступаю на конференциях. В ходе этого я просматриваю большое количество кода и ошибок. У меня был случай, когда в процессе написания статей, я выявил интересную закономерность, которую назвал "Эффект последней строки". Сейчас у меня дежавю. Я готов выделить ещё один паттерн ошибки, но с ним не всё так понятно, и мне интересно услышать мнение сообщества программистов.

Не так давно я делал две презентации (одну для C++, другую для C# программистов). Вдруг я понял, что в обоих случаях мне часто встречаются проблемы в небольших функциях сравнения, носящих такие имена как Compare, Cmp, Equal и так далее. При чем этот паттерн не зависит от языка. Я начал просматривать имеющуюся у меня коллекцию ошибок, и мои подозрения подтвердились. Теперь я могу заявить следующее:

Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.

Однако, с объяснением этого эффекта не всё так прозрачно как в случае с "Эффектом последней строки". Тот эффект я могу объяснить тем, что ослабляется внимание. Почему же так много ошибок в функциях сравнения? Моё предположение: программисты считают такие функции простыми и однотипными и соответственно пишут их очень быстро, не удосуживаясь проверить. Они уверены, что функции просты, а значит программистам и в голову не приходит их проверять.

Я не знаю, прав я или нет. Мне очень интересно услышать, что думаю другие разработчики о замеченном эффекте. Думаю, это поможет написать мне интересную статью.

Чтобы не быть голословным, я приведу несколько примеров ошибок. Начнем с самых показательных:

C++ (проект Samba):

static int compare_procids(const void *p1, const void *p2)
{
  const struct server_id *i1 = (struct server_id *)p1;
  const struct server_id *i2 = (struct server_id *)p2;

  if (i1->pid < i2->pid) return -1;
  if (i2->pid > i2->pid) return 1;
  return 0;
}


C# (проект IronPython and IronRuby):

public static int Compare(SourceLocation left,
                          SourceLocation right) {
  if (left < right) return -1;
  if (right > left) return 1;
  return 0;
}


У меня много накопилось примеров, и в статье будет бОльшее количество. Сейчас приведу только некоторые, чтобы вопрос не был слишком длинный. Поверьте, раз я обратил на такие функции внимание, в этом во всём действительно есть какая-то закономерность.

С++ (MySQL) Длинный, но однотипный код.

static int rr_cmp(uchar *a,uchar *b)
{
  if (a[0] != b[0])
    return (int) a[0] - (int) b[0];
  if (a[1] != b[1])
    return (int) a[1] - (int) b[1];
  if (a[2] != b[2])
    return (int) a[2] - (int) b[2];
  if (a[3] != b[3])
    return (int) a[3] - (int) b[3];
  if (a[4] != b[4])
    return (int) a[4] - (int) b[4];
  if (a[5] != b[5])
    return (int) a[1] - (int) b[5];        /// <<<
  if (a[6] != b[6])
    return (int) a[6] - (int) b[6];
  return (int) a[7] - (int) b[7];
}


С++ (Expat)

static int
nsattcmp(const void *p1, const void *p2)
{
  const XML_Char *att1 = *(const XML_Char **)p1;
  const XML_Char *att2 = *(const XML_Char **)p2;
  int sep1 = (tcsrchr(att1, NSSEP) != 0);
  int sep2 = (tcsrchr(att1, NSSEP) != 0);     /// <<<
  if (sep1 != sep2)
    return sep1 - sep2;
  return tcscmp(att1, att2);
}


C++ (CryEngine 3 SDK)

inline bool operator != (const SEfResTexture &m) const
{
  if (stricmp(m_Name.c_str(), m_Name.c_str()) != 0 ||       /// <<<
      m_TexFlags != m.m_TexFlags || 
      m_bUTile != m.m_bUTile ||
      m_bVTile != m.m_bVTile ||
      m_Filter != m.m_Filter ||
      m_Ext != m.m_Ext ||
      m_Sampler != m.m_Sampler)
    return true;
  return false;
}


C++ (G3D Content Pak). Подсказка: не там поставлена закрывающаяся круглая скоба.

bool Matrix4::operator==(const Matrix4& other) const {
  if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
    return true;
  }
  ...
}


С++ (RunAsAdmin Explorer Shim)

bool IsLuidsEqual(LUID luid1, LUID luid2)
{
  return (luid1.LowPart == luid2.LowPart) &&
         (luid2.HighPart == luid2.HighPart);
}


С++ (Serious Engine)

  inline BOOL IsEqual( CTexParams tp) {
    return tp_iFilter     == tp.tp_iFilter &&
           tp_iAnisotropy == tp_iAnisotropy &&       /// <<<
           tp_eWrapU      == tp.tp_eWrapU &&
           tp_eWrapV      == tp.tp_eWrapV; };


С# (MonoDevelop) Вновь длинная функция, но код весьма прост.

private bool MembersMatch(ISymbol member1, ISymbol member2)
{
  if (member1.Kind != member2.Kind)
  {
    return false;
  }

  if (member1.DeclaredAccessibility != 
      member1.DeclaredAccessibility 
   || member1.IsStatic != member1.IsStatic)      /// <<<
  {
    return false;
  }

  if (member1.ExplicitInterfaceImplementations().Any() ||  
      member2.ExplicitInterfaceImplementations().Any())
  {
    return false;
  }

  return SignatureComparer
    .HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors(
       member1, member2, this.IsCaseSensitive);
}
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: T4r4sB Россия  
Дата: 21.04.16 13:31
Оценка: -1
Здравствуйте, Analytic2007, Вы писали:

A> const struct server_id *i1 = (struct server_id *)p1;

A> const struct server_id *i2 = (struct server_id *)p2;

Это компилируется вообще? const void* переводится в T* без константы, это как?
Ну и убоищный структ перед названием типа.
Сдаётся мне, это сишка, а вовсе не кресты.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: _DAle_ Беларусь  
Дата: 21.04.16 13:42
Оценка: +9
Здравствуйте, Analytic2007, Вы писали:

A>Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.


Честно говоря, я бы оставил просто "Программисты часто допускают глупые ошибки". И в простых функциях, и в сложных.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: ro_man  
Дата: 22.04.16 06:41
Оценка: +3 -1
Здравствуйте, Analytic2007, Вы писали:

A>Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.


A>Однако, с объяснением этого эффекта не всё так прозрачно как в случае с "Эффектом последней строки". Тот эффект я могу объяснить тем, что ослабляется внимание. Почему же так много ошибок в функциях сравнения? Моё предположение: программисты считают такие функции простыми и однотипными и соответственно пишут их очень быстро, не удосуживаясь проверить. Они уверены, что функции просты, а значит программистам и в голову не приходит их проверять.



Кладут болт на юнит-тесты, ваш КО.
Я не сторонник стремиться покрыть юнит-тестами весь код, но это один из тех случаев, когда они незаменимы.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: ononim  
Дата: 23.04.16 10:41
Оценка:
A>Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.
errare humanum est, ignoscere divinum
Как много веселых ребят, и все делают велосипед...
Re: Почему люди часто ошибаются при написании простых функци
От: Pavel Dvorkin Россия  
Дата: 25.04.16 06:22
Оценка: +1
Здравствуйте, Analytic2007, Вы писали:

if (i1->pid < i2->pid) return -1;

Так, это готово,сейчас противоположный вариант — и все.
Копипастим.

if (i1->pid < i2->pid) return -1;

Теперь поправить надо. Поменять местами

if (i2->pid < i2->pid) return -1;

Черт, что я делаю. Зачем местами-то менять, когда достаточно меньше на больше заменить, и — 1 на 1

if (i2->pid > i2->pid) return 1;

Все,дописываем return 0; и можно сутью дела заниматься, а не этими пустяками.
With best regards
Pavel Dvorkin
Отредактировано 25.04.2016 6:28 Pavel Dvorkin . Предыдущая версия .
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: vsb Казахстан  
Дата: 25.04.16 07:58
Оценка: -1 :)
Это из-за невыразительности языка и/или стандартной библиотеки, а также отсутствия привычки писать понятный код.

static int compare_procids(const void *p1, const void *p2)
{
  const struct server_id *i1 = (struct server_id *)p1;
  const struct server_id *i2 = (struct server_id *)p2;

  if (i1->pid < i2->pid) return -1;
  if (i2->pid > i2->pid) return 1;
  return 0;
}


В данном коде я вижу сразу две проблемы: во-первых используется странное соглашение, что функция compare может возвращать только -1, 0 и 1, а не <0, 0, >0 (проблема библиотеки). Если убрать это странное соглашение, то последние строки можно заменить на

  return i1->pid - i2->pid;


а ещё лучше на

  return Integer.compare(i1->pid, i2->pid);


Если же апгрейдить наш язык, добавив в него лямбды (ну и generics, конечно), эту функцию можно было бы записать как

compare_procids = new_comparator<server_id *>(_.pid);


и, наверное, использовать уже в конкретном месте, а не объявлять глобально. Тут уж точно ошибиться негде.

static int rr_cmp(uchar *a,uchar *b)
{
  if (a[0] != b[0])
    return (int) a[0] - (int) b[0];
  if (a[1] != b[1])
    return (int) a[1] - (int) b[1];
  if (a[2] != b[2])
    return (int) a[2] - (int) b[2];
  if (a[3] != b[3])
    return (int) a[3] - (int) b[3];
  if (a[4] != b[4])
    return (int) a[4] - (int) b[4];
  if (a[5] != b[5])
    return (int) a[1] - (int) b[5];        /// <<<
  if (a[6] != b[6])
    return (int) a[6] - (int) b[6];
  return (int) a[7] - (int) b[7];
}


Ну этот код уж точно нарушил святую заповедь "не копипасть". Переписываем как
static int rr_cmp(uchar *a,uchar *b)
{
  for (size_t i = 0; i < 8; i++) {
    if (a[i] != b[i]) return (int) a[i] - (int) b[i];
  }
  return 0;

и всё. Можно ещё итераторы изобразить, чтобы исключить ошибки с индексами, но для C это, наверное, уже чересчур будет.

С другими вариантами похожая история. Чуть меньше копипасты, чуть больше библиотечного кода и все баги испаряются.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.04.16 17:36
Оценка:
Здравствуйте, Analytic2007, Вы писали:

A>Чтобы не быть голословным, я приведу несколько примеров ошибок.


Я правильно понял, что все ошибки имеют одну и ту же механику — скопировали кусок кода, что-то поправили, но не все или не совсем правильно?

Если это так, то эффект ожидаем. Когда человек пишет код или просто текст, он мысленно проговаривает его, осмысливает.
При операциях копирования и вставки такое осмысление не происходит. Набор слов и операторов не складываются в голове в единую логическую конструкцию.
Поэтому правки кода после копипасты зачастую неосмысленные.

С другой стороны маленькие функции, которые должны быть очень простые, не вызывают пристального внимания читающих. Мелкие ошибки остаются незамеченными очень долго.
Re[2]: Почему люди часто ошибаются при написании простых функций сравнения?
От: Analytic2007 Россия https://www.viva64.com/ru/pvs-studio/
Дата: 27.04.16 19:36
Оценка:
G>Я правильно понял, что все ошибки имеют одну и ту же механику — скопировали кусок кода, что-то поправили, но не все или не совсем правильно?

Скорее всего именно так.

G>Если это так, то эффект ожидаем. Когда человек пишет код или просто текст, он мысленно проговаривает его, осмысливает.

G>При операциях копирования и вставки такое осмысление не происходит. Набор слов и операторов не складываются в голове в единую логическую конструкцию.
G>Поэтому правки кода после копипасты зачастую неосмысленные.

G>С другой стороны маленькие функции, которые должны быть очень простые, не вызывают пристального внимания читающих. Мелкие ошибки остаются незамеченными очень долго.


Согласен. Сам склоняюсь к таким мыслям.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: Sinclair Россия https://github.com/evilguest/
Дата: 10.05.16 10:05
Оценка: +1
Здравствуйте, Analytic2007, Вы писали:

Мне кажется, что это частный случай более общей закономерности "интуитивно очевидные утверждения чаще оказываются неверными, чем утверждения сомнительные".
Сомнительные утверждения мы проверяем; интуитивно очевидные принимаем на веру.
Так и тут — для двухстрочных функций никто не озаботился написанием тестов и вообще мало-мальским доказательством корректности.
Вот если бы там было что-то посложнее, то наверняка программист бы не рискнул коммитить просто так, а задался бы либо внимательной вычиткой, либо peer review, либо нормальным тестовым покрытием.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: Кодт Россия  
Дата: 12.05.16 15:41
Оценка:
Здравствуйте, Analytic2007, Вы писали:

A>Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.


A>Однако, с объяснением этого эффекта не всё так прозрачно как в случае с "Эффектом последней строки". Тот эффект я могу объяснить тем, что ослабляется внимание. Почему же так много ошибок в функциях сравнения? Моё предположение: программисты считают такие функции простыми и однотипными и соответственно пишут их очень быстро, не удосуживаясь проверить. Они уверены, что функции просты, а значит программистам и в голову не приходит их проверять.


Написать аксиоматически верный компаратор, кроме лексикографического, — вообще муторное дело.
А лексикографический — там будет или магия (макросы, шаблоны, лямбды, трюки), либо копипасты (многажды — имена объектов, четырежды — каждое поле).

В магии легко облажаться, да и код на грани write only. (Это же всё велосипеды большей или меньшей степени затейливости).
В копипастах легко очепятаться.

Были бы почленные операторы сравнения из коробки (как в хаскеле удобно: data Something = ...... deriving Cmp) — была бы сказка.
Была бы троичная логика из коробки — опять же была бы сказка.
Но сказки, увы, нет.

И самое главное, что средств верификации — из коробки тоже нет, а покрывать тестами вместо верификации — ну... непросто.
Перекуём баги на фичи!
Re: Почему люди часто ошибаются при написании простых функций сравнения?
От: B0FEE664  
Дата: 12.05.16 16:17
Оценка: -1
Здравствуйте, Analytic2007, Вы писали:

A>Не так давно я делал две презентации (одну для C++, другую для C# программистов). Вдруг я понял, что в обоих случаях мне часто встречаются проблемы в небольших функциях сравнения, носящих такие имена как Compare, Cmp, Equal и так далее. При чем этот паттерн не зависит от языка. Я начал просматривать имеющуюся у меня коллекцию ошибок, и мои подозрения подтвердились. Теперь я могу заявить следующее:

A>Программисты часто допускают глупые ошибки в простых функциях, сравнивающих два объекта.

Кстати, говоря о таком коде хочу заметить, что множество этих ошибок можно было бы избежать за счёт правильного стиля оформления кода.
Я, например, придерживаюсь нижеизложенных правил и практически не допускаю ошибок на такие сравнение, за исключением проверок на равенство. Иногда путаю != с ==.
Итак, правила:
— однотипный код должен быть оформлен в таблицу;
— открывающая и закрывающая скобки должны быть либо в одной строке, либо в одном столбце;
— знаки сравнения > и => не используются;
— цифры не используются в идентификаторах.

Запишем код в соответствии с правилами:
A>
A>  if (i1->pid < i2->pid) return -1;
A>  if (i2->pid > i2->pid) return 1;
A>


Заменяем 1 и 2 на Left и Right, знак > на <. Получаем:

A>  if ( iLeft ->pid < iRight->pid ) return -1;
A>  if ( iRight->pid < iRight->pid ) return 1;

При такой записи даже думать не надо — сразу видно нарушение симметрии.

Тут тоже самое:
A>  if (left < right) return -1;
A>  if (right > left) return 1;

заменяем знак > на <. Получаем:
A>  if ( left < right ) return -1;
A>  if ( left < right ) return 1;


A>С++ (MySQL) Длинный, но однотипный код.

Должен быть в виде таблицы:

static int rr_cmp(uchar *a,uchar *b)
{
  if (a[0] != b[0])    return (int) a[0] - (int) b[0];
  if (a[1] != b[1])    return (int) a[1] - (int) b[1];
  if (a[2] != b[2])    return (int) a[2] - (int) b[2];
  if (a[3] != b[3])    return (int) a[3] - (int) b[3];
  if (a[4] != b[4])    return (int) a[4] - (int) b[4];
  if (a[5] != b[5])    return (int) a[1] - (int) b[5]; 
  if (a[6] != b[6])    return (int) a[6] - (int) b[6];
                       return (int) a[7] - (int) b[7];
}

ошибку видно?

  Скрытый текст
A>С++ (Expat)
A>
static int
A>nsattcmp(const void *p1, const void *p2)
A>{
A>  const XML_Char *att1 = *(const XML_Char **)p1;
A>  const XML_Char *att2 = *(const XML_Char **)p2;
A>  int sep1 = (tcsrchr(att1, NSSEP) != 0);
A>  int sep2 = (tcsrchr(att1, NSSEP) != 0);     /// <<<
A>  if (sep1 != sep2)
A>    return sep1 - sep2;
A>  return tcscmp(att1, att2);
A>}
A>


Но, в некоторых случаях стиль не спасает, как, например, тут:
static int
nsattcmp(const void *pLeft, const void *pRight)
{
  const XML_Char* attLeft  = *(const XML_Char **)pLeft;
  const XML_Char* attRight = *(const XML_Char **)pRight;
  int sepLeft  = (tcsrchr(attLeft, NSSEP) != 0);
  int sepRight = (tcsrchr(attLeft, NSSEP) != 0);

  if ( sepLeft != sepRight )
    return sepLeft - sepRight;
  return tcscmp(attLeft, attRight);
}


как и в остальных операциях сравнения на == и !=.

Кстати, эта ошибка:

A>
inline bool operator != (const SEfResTexture &m) const
A>{
A>  if (stricmp(m_Name.c_str(), m_Name.c_str()) != 0 ||       /// <<<
A>


и эта ошибка:

A>
  inline BOOL IsEqual( CTexParams tp) {
A>    return tp_iFilter     == tp.tp_iFilter &&
A>           tp_iAnisotropy == tp_iAnisotropy &&       /// <<<
A>           tp_eWrapU      == tp.tp_eWrapU &&
A>           tp_eWrapV      == tp.tp_eWrapV; };
A>


связаны с неудачным выбором имён: префикс поля совпадает с именем переменной.
И каждый день — без права на ошибку...
Re[2]: Почему люди часто ошибаются при написании простых функций сравнения?
От: PM  
Дата: 17.05.16 19:36
Оценка: :))
Здравствуйте, Кодт, Вы писали:

К>Написать аксиоматически верный компаратор, кроме лексикографического, — вообще муторное дело.

К>А лексикографический — там будет или магия (макросы, шаблоны, лямбды, трюки), либо копипасты (многажды — имена объектов, четырежды — каждое поле).

К>В магии легко облажаться, да и код на грани write only. (Это же всё велосипеды большей или меньшей степени затейливости).

К>В копипастах легко очепятаться.

К>Были бы почленные операторы сравнения из коробки (как в хаскеле удобно: data Something = ...... deriving Cmp) — была бы сказка.

К>Была бы троичная логика из коробки — опять же была бы сказка.
К>Но сказки, увы, нет.

В С++11 можно немного приблизиться к сказке, перечислив члены структуры для компаратора только один раз, а затем использовать в компараторе указатели на эти члены: http://playfulprogramming.blogspot.ru/2016/01/a-flexible-lexicographical-comparator.html

Код идеи оттуда:
template <typename Comparator, typename ... Ms>
class memberwise_comparator : private Comparator
{
public:
  memberwise_comparator(std::tuple<Ms...> t) : ms(t) {}
  template <typename T>
  bool operator()(T const& lh, T const& rh) const
  {
    using idxs = std::make_index_sequence<sizeof...(Ms)>;
    return compare(idxs{}, lh, rh);
  }
private:
  template <size_t ... Ns, typename T>
  bool compare(std::index_sequence<Ns...>, T const& lh, T const& rh) const
  {
    const Comparator& comp = *this;
    return comp(std::tie(lh.*std::get<Ns>(ms)...),
                std::tie(rh.*std::get<Ns>(ms)...));
  }
  std::tuple<Ms...> ms;
};

template <typename Comp, typename ... T>
auto lex_compare(T ... t)
{
  return memberwise_comparator<Comp, T...>{std::make_tuple(t...)};
}

struct S
{
  int a;
  int b;
  int c;
};

bool is_sorted(std::vector<S> const& v)
{
  auto comparator = lex_compare<std::less<>>(&S::b, &S::a);
  return std::is_sorted(std::begin(v), std::end(v), comparator);
}
Re[3]: Почему люди часто ошибаются при написании простых функций сравнения?
От: Кодт Россия  
Дата: 17.05.16 20:19
Оценка:
Здравствуйте, PM, Вы писали:

К>>В магии легко облажаться, да и код на грани write only. (Это же всё велосипеды большей или меньшей степени затейливости).


PM>В С++11 можно немного приблизиться к сказке, перечислив члены структуры для компаратора только один раз, а затем использовать в компараторе указатели на эти члены: http://playfulprogramming.blogspot.ru/2016/01/a-flexible-lexicographical-comparator.html


См.выше про магию...
Я таких способов штук шесть накидать могу (и, собственно, хотел запостить, но потом стёр, как Гоголь второй том).

С кортежами и вариадиками вообще прекрасно:
template<class Obj, class... Members>
bool memberwise_less(const Obj& a, const Obj& b, Member... members) {
  return std::tie(a.*members...) < std::tie(b.*members...); // tie, а не make_tuple, чтобы по ссылкам - чтобы не копировать
}


Но ведь потом захочется прекрасного: сравнения полей по ключам, т.е.
template<class Obj, class... KeyFuns>
bool keyfun_less(const Obj& a, const Obj& b, KeyFuns... keys) {
  return std::tie(keys(a)...) < std::tie(keys(b)...);
}

потом вспомнить о ленивости и вернуться к рекурсивным шаблонам
template<class Obj>
bool keyfun_less(const Obj& a, const Obj& b) { return false; }

template<class Obj, class KeyFun, class... KeyFuns>
bool keyfun_less(const Obj& a, const Obj& b, KeyFun key, KeyFuns... keys) {
  {
    auto&& ka = key(a);
    auto&& kb = key(b);
    if (ka < kb) return true;
    if (kb < ka) return false;
  } // отбрасываем временные переменные
  return keyfun_less(a, b, keys...);
}

И, в итоге, получим какую-нибудь новую библиотечку в составе буста. Заодно, прибегнув к препроцессору, сможем родить код, совместимый даже с C++03. (А кстати, нет ли среди него чего-нибудь уже готового?)
Перекуём баги на фичи!
Re[4]: Почему люди часто ошибаются при написании простых функций сравнения?
От: PM  
Дата: 18.05.16 13:03
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, PM, Вы писали:


К>>>В магии легко облажаться, да и код на грани write only. (Это же всё велосипеды большей или меньшей степени затейливости).


PM>>В С++11 можно немного приблизиться к сказке, перечислив члены структуры для компаратора только один раз, а затем использовать в компараторе указатели на эти члены: http://playfulprogramming.blogspot.ru/2016/01/a-flexible-lexicographical-comparator.html


К>См.выше про магию...

К>Я таких способов штук шесть накидать могу (и, собственно, хотел запостить, но потом стёр, как Гоголь второй том).

Понятно. Мой персональный выбор — использовать std::tie, потому что в 99% случаев требуется лишь operator== и operator< для структуры с несколькими полями.

К>И, в итоге, получим какую-нибудь новую библиотечку в составе буста. Заодно, прибегнув к препроцессору, сможем родить код, совместимый даже с C++03. (А кстати, нет ли среди него чего-нибудь уже готового?)


Есть boost.fusion как заменитель compile-time reflection. Адаптировав для fusion структуру, можно в том числе получить и почленное сравнение:

struct Foo
{
   int a, b, c;
};

BOOST_FUSION_ADAPT_STRUCT(Foo, (int, a)(int, b)(int, c))

bool operator<(Foo const& x, Foo const& y) { return boost::fusion::less(x, y); }


Но это на любителя.
Re: Какие еще люди? :)
От: rm822 Россия  
Дата: 25.08.16 20:40
Оценка:
Это ж Джагжит, у него половина компареров глючные, и больше 3 мес он нигде не работает
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.