-1

Edit Solution::

In fact, i juste forget the placment new in the copy constructor ><"

Question:

I have a weird problem. After having tried for a long momnet origin I found masi does not understand. If someone can explain to me why.

My class:

class B; //on other file
class A {
   public:
     A(int type) : type(type)
     {
        switch(type)
        {
           case TOKEN:
           {
             for(int i=0;i<4;++i)
                new(&token.h[i].link) shared_ptr<B>; //< init the ptr on the addr (because of union)
           }break;
           case OTHER: {}break;
        }
     }
     ~A()
      {
        switch(type)
        {
            case TOKEN:
            {
             for(int i=0;i<4;++i)
             {
                /*option 1*/ token.h[i].link.~shared_pt<B>(); //< Make seg fault
               /*option 2*/ token.h[i].link.reset(); //< ok
             }
            }break;
            case OTHER: {}break;
         }
        }
      }
   enum {TOKEN=0,OTHER} type;

   union {
       struct {
           double score;
           struct {
               std::shared_ptr<B> link;
               double to_find;
               } h [4];
       }token;

       struct {
          //else
       } other;
   }
};

My code:

void f()
{
    vector<A> vec;
    A tmp = A(A::TOKEN);
    vec.emplace_back(tmp);
}

Option 1: this causes an error when leaving f; option 2: Ok but ~shared_ptr() is not call, so it make memory leak, right?

If you have an idea that could help me understand who is wrong.

Edit: I use C++11 with gcc.4.6.3 on Ubuntu 12.04x86.

Original code:

    class stack_token {
        public:
            stack_token();
            stack_token(const stack_token& other);
            stack_token(const int i,Parser::peptide::peak* data); //peak
            stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak); //aa
            stack_token(const int i); //aa pour boucher un trou
            stack_token(const double score); //HEADER

            ~stack_token();

            stack_token& operator=(const stack_token& other);

            inline stack_token* get_peak_stack_NULL() {
                stack_token* res = aa_token.pt_data;
                aa_token.pt_data=NULL;
                return res;
            };

            void __print__() const;


            enum Type {UNKNOW=-1,AA_TOKEN=0,AA_HOLD_TOKEN,/*AA_LIST,*/PEAK_TOKEN, HEADER_TOKEN} type;

            union {
                struct  {
                    int index;
                    double error;
                    stack_token* pt_data;
                } aa_token;

                struct{
                    double error;
                    stack_token* pt_data;
                    std::vector<int> aa_index;
                } aa_hold_token;

                struct {
                    int index;
                    Parser::peptide::peak* pt_data;
                } peak_token;

                struct {
                    double score;
                    struct {
                        std::shared_ptr<std::list<list_arg> > link;
                        double to_find;
                    } holds [Parser::peptide::SIZE];
                } header_token;
            };
    };

 stack_token::~stack_token()
{
switch(type)
{
    case AA_TOKEN:
    {
       if(aa_token.pt_data != NULL)
            delete aa_token.pt_data;
    }break;

    case AA_HOLD_TOKEN :
    {
        aa_hold_token.aa_index.~vector<int>();
    }break;

    case PEAK_TOKEN : 
    {
    }break;

    case HEADER_TOKEN : 
    {
       for (int i=0;i<Parser::peptide::SIZE;++i)
            header_token.holds[i].link.reset();//~shared_ptr<std::list<list_arg> >();
    }break;

    default : break;
}
};


  stack_token::stack_token()
{
this->type = UNKNOW;
};

stack_token::stack_token(const int i,Parser::peptide::peak* data) //peak
{
this->type=PEAK_TOKEN;
peak_token.index = i;
peak_token.pt_data = data;
};

stack_token::stack_token(const int i,const double e,AnalyseurPeptide::stack_token* peak) //aa
{
this->type=AA_TOKEN;
aa_token.error =e;
aa_token.index = i;
aa_token.pt_data = peak;
};

stack_token::stack_token(const int i)
{
this->type=AA_HOLD_TOKEN;
aa_hold_token.error = 0;
aa_hold_token.pt_data = this;
new(&aa_hold_token.aa_index) vector<int>();
};


stack_token::stack_token(const double score) //HEADER
{
this->type = HEADER_TOKEN;
header_token.score = score;
for (int i=0;i<Parser::peptide::SIZE;++i)
    new (&header_token.holds[i].link) shared_ptr<list<list_arg> >;
#warning "add to_find init"
};

Code that fail:

void save_stack(const std::list<stack_token*>& search, std::list<std::vector<stack_token> >& res)
{
    vector<AnalyseurPeptide::stack_token> l;
    auto i=search.begin();
    auto end = search.end();

    stack_token tmp = stack_token(0.f); /* if I remove this */
    l.emplace_back(tmp); /* and this, all is ok */

    while(i!=end)
   {
     l.emplace_back(**i); //< fail here
      ++i;
   }
   res.emplace_back(l);
}
Krozark
  • 862
  • 9
  • 27
  • 6
    Why use placement new? Why not simple assign the result of [`std::make_shared`](http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared)? – Some programmer dude Jan 16 '13 at 16:54
  • 1
    Looks like a textbook case of "overthinking it"? – Kerrek SB Jan 16 '13 at 16:57
  • @JoachimPileborg placement new is requered beca&use of the union with object inside. – Krozark Jan 16 '13 at 17:00
  • 3
    std::shred_ptr is a non-pod type and thus may not be used inside a union. The use of non-pods inside unions gives undefined behavior. – Arne Mertz Jan 16 '13 at 17:00
  • 2
    @ArneMertz I use unrestrected union (c++11) – Krozark Jan 16 '13 at 17:01
  • 1
    @Krozark ok, but does your compiler implement that correctly already? Which compiler do you use? And could you post the original code, reduce to the necessary minimum? I can tell you did not exactly copy&paste it because of the typos in `switch` – Arne Mertz Jan 16 '13 at 17:04
  • this code is not smart but evil. avoid this type of union. There are very few good designs with unions. – Walter Jan 16 '13 at 17:04
  • @ArneMertz this is already the minimum code. – Krozark Jan 16 '13 at 17:05
  • @Krozark why use a union at all? Since the `other` part is empty, you could simply skip the union and leave the token struct blank if type is OTHER. – Arne Mertz Jan 16 '13 at 17:08
  • @ArneMertz So I add my original code, where the other part of the union is not empty. I'v a problem juste with shared_ptr, not other, and I don't know why. – Krozark Jan 16 '13 at 17:13
  • 1
    Your code is illegal. In C++03, of course, types with non-trivial constructors, assignment operators or destructors cannot be members of a `union`. In C++11, if the `union` contains any such elements, the corresponding functions are deleted unless they are user defined. And with no constructors, you cannot create an instance of your union. – James Kanze Jan 16 '13 at 17:17
  • @ArneMertz: This is trivially implementable in any compiler, since the standard only states that what was undefined behavior has now defined behavior in the simplest way: the programmer must handle this manually (i.e. the compiler does nothing and it is up to the user to call the destructor when needed) – David Rodríguez - dribeas Jan 16 '13 at 17:19
  • Didn't see the union first. Unions are, in my opinion, kind of a hack these days, and there are often better solutions than resorting to unions. How about something simple such as a base class with common functionality/abstract methods and specific child classes (e.g. the `token` structure)? – Some programmer dude Jan 16 '13 at 17:21
  • @DavidRodríguez-dribeas trivially implementable does not mean it is already implemented. The link Joachim posted suggests that the union must have user defined special members since the anonymous structs are nontrivial. Since the compiler does not complain about the missing UD-special members, it may be that unrestricted unions are not implemented correctly. Maybe you can solve your problem if you provide those special members as well for the anonymous union members as for the union itself. – Arne Mertz Jan 16 '13 at 17:32
  • 1
    -1 not the real code (e.g. `swithc` would never compile). – Cheers and hth. - Alf Jan 16 '13 at 17:34
  • @ArneMertz Unrestricted unions are clearly not implemented correctly. So he can't use them. – James Kanze Jan 16 '13 at 17:37
  • @DavidRodríguez-dribeas There was no undefined behavior in his class. It has errors which require a compiler diagnostic, both in C++03 (non-PODs in a union) and in C++11 (the resulting union has no constructors, and is used in contexts where it must be constructed). The using code does have undefined behavior: he has instantiated `std::vector` on a class which doesn't support copy or assignment. (The presence of the `union` in the class means that the compiler cannot generate them.) – James Kanze Jan 16 '13 at 17:39
  • @JamesKanze: I just meant that IIRC the compiler does not need to add anything to support unrestricted unions, not that the code in the question is correct, just that the compiler does not need to change in any way other than potentially changing diagnostics. – David Rodríguez - dribeas Jan 16 '13 at 17:42
  • 3
    Just to make it clear: it was not undefined behavior for union members to have non-trivial constructors et al. The error required a diagnostic. And in C++11, a union with a member which has a non-trivial default constructor will require a diagnostic if there is any attempt to construct it without any arguments. For example, in the initialization of `A`. – James Kanze Jan 16 '13 at 17:51
  • @JamesKanze: Thanks, will need to look further into this.1 – David Rodríguez - dribeas Jan 16 '13 at 18:01

2 Answers2

2

If you're compiling with C++03, the code is illegal, because C++03 doesn't allow types with non-trivial default constructors, copy constructors, assignment operators or destructors in a union. With C++11, the code is illegal, because if the union contains any of the above, the compiler deletes the corresponding member of the union. So your union has no default constructor, copy constructor, assignment or destructor. Which means you can't instantiate it, or use it in any way. And which means that the default constructor needed by A::A(int) doesn't exist, and that the compile should complain when you define this function (or any constructor of A).

If the compiler compiles such code, it means that the compiler doesn't implement the new union stuff correctly, and thus, that you cannot use it.

With regards to what actually happens: I suspect that the compiler is using bitwise copy in the copy constructor of A (rather than refusing to generate it). vec.emplace_back(tmp) uses the copy constructor to create the new element in vec. Bitwise copy means that you end up with two instances of a shared_ptr which point to the same object, but which both have a count of 1. The first one destructs correctly, and the second accesses deleted memory. Boom.

The simplest way to solve your problem is to use boost::variant (which means defining the struct in the union somewhere outside of the union, and giving them a name). If for some reason you cannot use Boost, it's relatively trivial to implement by hand, along the lines of what you are doing. In the union itself, you just have unsigned char token[ sizeof(TokenType) ]; etc., for each non-POD member, with some additional members if necessary to ensure alignment (on most processors, a double will do the trick). You then use reinterpret_cast on the name of the array to get a pointer to the desired type, placement new to initialize it, and explicit destruction to destruct it, much along the lines you've done. And you implement a copy constructor and an assignment operator that work, and take into account the types as well.

(It's not that difficult. I've done it one or two times: for tokens in a parser, for modeling tables which we get from Excel, etc.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • note that the given syntactically incorrect code can't compile, so (I think) very little can be said from its property of compiling or not. also i would hesitate about recommending using e.g. `boost::variant` to solve a problem, because *we don't know what problem* the union is meant to solve. it's not the usual token thing. – Cheers and hth. - Alf Jan 16 '13 at 17:40
  • @Cheersandhth.-Alf - note that he added his original code later since we drew wrong conclusions from that incorrect example code. – Arne Mertz Jan 16 '13 at 17:44
  • i don't want to use boost. C++11 make the job. – Krozark Jan 16 '13 at 18:14
  • @Krozark Boost is an excellent library that extends and complements many C++11 features. Many of the new library features _in_ C++11 comes from Boost, and the same will probably happen for the next version of the C++ standard as well. What I'm saying is that you can use both, if a feature in in the standard use that otherwise Boost. – Some programmer dude Jan 16 '13 at 23:39
  • @JoachimPileborg Yes I know. But in fact i don't need boost to solve this so... – Krozark Jan 17 '13 at 10:17
  • @Krozark No. But without Boost, you need to reimplement something that is already in Boost. And while this isn't conceptually difficult (as explained in my answer), it _is_ a lot of boilerplate code to write, it's very, very easy to get it wrong, and it's a real pain to debug. – James Kanze Jan 17 '13 at 11:18
  • In fact, i forget placement new in copy constructor ><" – Krozark Jan 17 '13 at 15:20
  • The devil in this detail might be (on some systems?!) alignment. With c++11 I think there are means to specify alignment. But to be really ultimatively portable, assuming that a ``unsigned char token [...]`` is always correctly aligned is ... daring. – BitTickler Mar 20 '16 at 06:57
1

Technical problems:

  • union (don't),
  • uninitialized,
  • rule of three (not taking properly charge of copying)

Design problems:

  • Representing types as numbers. Represent types as types.

Keep the knowledge you gained from writing that code, and start from scratch again.

Very little more can be meaningfully said until you post the real code (e.g. swithc will never compile: what you posted is not the real code).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • There are a few special cases where discriminate unions are appropriate. Tokens from a parser (where the semantic value may have different types) are a good example. – James Kanze Jan 16 '13 at 17:35
  • @JamesKanze: i agree, and this example *involves* tokens, so one might offhand think that it would be about that. but it isn't. not sure what the `OTHER` case is (can you think of a less descriptive name), but it's not that. the union in this code is just dumb. – Cheers and hth. - Alf Jan 16 '13 at 17:37
  • The other case that I've encountered is handling data that comes from Excel, Python or Mathematica. All of which allow tables/lists of mixed types. In his actual example, the word `token` appears in some of the names, so I'm guessing that that's what he's trying to do. But I sort of agree concerning the union in his code; when you need a discriminate union, you don't do it with anonymous unions with anonymous structs. Given all that you have to do around it, you define each type with an explicit name. – James Kanze Jan 16 '13 at 17:44
  • @Cheersandhth.-Alf thx, but It's not a answer. And i need union, so don't say ro not use them. – Krozark Jan 16 '13 at 17:59
  • first i post the minimal code, then the real.Just open your eyes. – Krozark Jan 16 '13 at 18:13
  • @Krozark: sorry i stopped scanning after seeing that the code was bogus. but anyway, the main *technical* problem is that you don't deal properly with copying. and the "real" code does not contain the copy constructor definition, i.e. it's **incomplete**, missing the most important part for those who focus solely on the technical problem. almost as if you intentionally omitted just that part, like a troll. but anyway, solving that problem would be like finding a way to deal with the problems of triangular wheels for a car. don't: use round wheels (no silly union) – Cheers and hth. - Alf Jan 16 '13 at 18:26
  • @Cheersandhth.-Alf Sorry, but Copy constructor is deefined (stack_token(const stack_token& other);). But i don't put the cod of it. (And i need union, so please don't say me to not use it) – Krozark Jan 17 '13 at 10:10