Utilisation de BOOST_STRONG_TYPEDEF pour différencier les types d'arg mais provoquant une erreur de segment

Jan 09 2021

J'ai une méthode nécessitant plusieurs variables du même type d'énumération. Pour permettre au compilateur de détecter si je passe le mauvais argument que j'utilise BOOST_STRONG_TYPEDEF. Cependant, j'obtiens un défaut de segmentation lorsque je crée une instance et que je compare dans une instruction IF.

La version Boost est 1.74

enum class Testable
{
    UNDEFINED,
    A,
    B
};

BOOST_STRONG_TYPEDEF(Testable, SomeType)

int main()
{  
    SomeType abc{Testable::UNDEFINED};
    std::cout << "START" << std::endl;
    
    if(abc == Testable::UNDEFINED)  // Seg faults here
    {
        volatile int j = 0;
    }
    
    std::cout << "FINISH" << std::endl;
}

GDB backtrace suggère qu'il s'agit d'un débordement de pile / appel récursif:

#1    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#2    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#3    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#4    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#5    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:
#6    0x00007ffff74c5d9d in boost::operators_impl::operator== (y=@0x7fffffcc9e44:

Il n'y a pas beaucoup de documentation pour BOOST_STRONG_TYPEDEF. Est-ce que je l'utilise mal?

La version Boost est 1.74. J'utilise Clang.

Réponses

1 sehe Jan 09 2021 at 20:09

Le désinfectant dit

==3044==ERROR: AddressSanitizer: stack-overflow on address 0x7ffcc58b3ff8 (pc 0x56310c340e84 bp 0x7ffcc58b4000 sp 0x7ffcc58b3ff
0 T0)
    #0 0x56310c340e84 in boost::operators_impl::operator==(Testable const&, SomeType const&) /home/sehe/custom/boost_1_75_0/boo

Le problème est que STRONG_TYPEDEF de Boost rend le type dérivé totalement ordonné avec le type de base:

struct SomeType
    : boost::totally_ordered1<SomeType, boost::totally_ordered2<SomeType, Testable>>
{
    Testable t;
    explicit SomeType(const Testable& t_) noexcept((boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_) {}
    SomeType() noexcept( (boost::has_nothrow_default_constructor<Testable>::value)) : t() {}
    SomeType(const SomeType& t_) noexcept( (boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_.t) {}
    SomeType& operator=(const SomeType& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs.t;
        return *this;
    }
    SomeType& operator=(const Testable& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs;
        return *this;
    }
    operator const Testable&() const { return t; }
    operator Testable&() { return t; }
    bool operator==(const SomeType& rhs) const { return t == rhs.t; }
    bool operator<(const SomeType& rhs) const { return t < rhs.t; }
};

Si vous supprimez cette source de conversion implicite:

struct SomeType
    : boost::totally_ordered1<SomeType
      /*, boost::totally_ordered2<SomeType, Testable>*/>
{
     // ...

il JustWorks (TM). Je dirais que vous devriez également créer les opérateurs de conversion explicitet toujours faire les moulages:

En direct sur Coliru

#include <boost/serialization/strong_typedef.hpp>
#include <iostream>

enum class Testable { UNDEFINED, A, B };
       
struct SomeType
    : boost::totally_ordered1<SomeType
      /*, boost::totally_ordered2<SomeType, Testable>*/>
{
    Testable t;
    explicit SomeType(const Testable& t_) noexcept((boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_) {}
    SomeType() noexcept( (boost::has_nothrow_default_constructor<Testable>::value)) : t() {}
    SomeType(const SomeType& t_) noexcept( (boost::has_nothrow_copy_constructor<Testable>::value)) : t(t_.t) {}
    SomeType& operator=(const SomeType& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs.t;
        return *this;
    }
    SomeType& operator=(const Testable& rhs) noexcept((boost::has_nothrow_assign<Testable>::value)) {
        t = rhs;
        return *this;
    }
    explicit operator const Testable&() const { return t; }
    explicit operator Testable&() { return t; }
    bool operator==(const SomeType& rhs) const { return t == rhs.t; }
    bool operator<(const SomeType& rhs) const { return t < rhs.t; }
};

int main() {
    SomeType abc{ Testable::UNDEFINED };
    std::cout << "START" << std::endl;

    if (abc == SomeType{Testable::UNDEFINED}) {
        volatile int j = 0;
    }

    std::cout << "FINISH" << std::endl;
}