views:

502

answers:

2

In Bison I have a union

%union
{
   std::string* sval;
}

And I want to use it like this

In Lex:

*(yylval->sval) = "ABCD";

Rather than

yylval->sval = new std::string("ABCD");

To prevent memory leaks easily

However I need some way to allocated a std::string to sval to begin with.

How can I do that?

A: 

I'm not entirely clear why you want to do this but I can see why what you have isn't working. It's because "ABCD" is a const char *, not a std::string.

I know with Yacc that the first "%{ ... %}" section allows you to define C stuff outside the control of Yacc (and it appears Bison has a similar feature, based on it's upward compatibility claim and the documentation here, 2.1.1). Why would you not put:

std::string *strABCD = new std::string("ABCD");

in that section and then use:

yylval->sval = strABCD;

later on whenever you needed a pointer to that string?

That seems to me to be the easiest way to achieve what (I think) you want.

If you're worried about allocations within the Bison parser not being freed (and they should be), my advice is to not do them in there. You can set up your string before calling yyparse() then free it after return.

Update:

Here's how I'd do it to avoid allocating/freeing that fixed value within the Bison parser. Set it up as a global that lives for the duration of the program.

Main code:

std::string *strABCD = new std::string ("ABCD");

int main(...) {
    // Do some stuff.
    : : :
    yyparse();
    : : :
    // Do some other stuff.
}

Bison parser source:

%{
    extern std::string *strABCD;
%}
: : :
yylval->sval = strABCD;

That returns the fixed pointer to your ABCD string with no allocation or freeing in the Bison code at all (and precious little even in the main code).

paxdiablo
Because now you have a memory leak unless you free your inputs to Yacc for every rule matches.
DevDevDev
Okay, but if you're going to allocate memory then you need to free it. If you don't want that, don't allocate - change your union to be a char* instead. Most parsers I've written were one-shots so the prelude code "leaking" one std::string would be irrelevant. Your architecture may be different. Alternatively set it up before calling the parser and tear it down afterwards. See the update.
paxdiablo
Your code will leak one string for every rule matched with a return type.
DevDevDev
@SteveM, see the final paragraph. If you don't want to allocate within the Bison parser, don't. Create a single "std::string *strABCD" before doing each yyparse() and free it after return (or create it once on program start and free on program exit). Voila, no leaks.
paxdiablo
Thanks Pax, can you tell me where in the program I can assign this? Would I not use the standard yylval then?
DevDevDev
Before your main code calls yyparse() is the place to create it. And you should be able to use yylval, depending on what you've set YYSTYPE to be. Updating answer again as per your request.
paxdiablo
+2  A: 

You can't safely put types with constructors or destructors (such as std::string) in a union, so this won't work.

What you can do instead is not use %union at all -- instead use a macro to map YYSTYPE directly to some other type:

%{
#define YYSTYPE std::string
%}

then yylval will be that type (as will all $n options in the grammar code)

Chris Dodd
Where do I do this? Also how do I define types of my rules?like%type <YYSTYPE> rule
DevDevDev
I tried it and now I'm getting "cannot convert from char* to semantic_type*"
DevDevDev
I don't think your code works.
DevDevDev
You put this in the top section, and with it, you don't define any %type rules, as everything is a YYSTYPE.
Chris Dodd