Welcome, Guest. Please login or register.
July 29, 2025, 09:31:01 AM

Login with username, password and session length

Search:     Advanced search
we're back, baby
*
Home Help Search Login Register
f13.net  |  f13.net General Forums  |  General Discussion  |  Topic: C/C++ Question 0 Members and 1 Guest are viewing this topic.
Pages: 1 [2] Go Down Print
Author Topic: C/C++ Question  (Read 6684 times)
Morat20
Terracotta Army
Posts: 18529


Reply #35 on: March 13, 2012, 05:38:33 PM

Yeah, it's like this right --

globals.h
extern std::string myString[2];

globals.cpp
std::string myString[] = {"One", "Two"};
std::string test = myString[1];


MyApp.cpp:
#include globals.h
#include globals.cpp

MyApp::OnInit() {
//blah  blah
}

After dealing with some weird-ass MSVC stuff (I'd accidentally included MyString.cpp in the solution, which meant it was compiled into a globals.obj, which meant when it came time to #include it into MyApp it threw the usual "already defined" crap -- so I removed it from the solution, so now it's included in the preprocessed MyApp -- just like it should be.

Whole thing starts up, goes to MyString.cpp and then vomits all over EVERYTHING.

So I blew up the extern and just tried to declare and define a std::string array and assign it (IE: pretend I never externed it globals.h didn't exist).

Get this -- it supposely allocates MyString (I don't think it does -- I checked it at run-time in the debugger, and the allocation looks really wrong. I'm new to MSVC so maybe I just can't read it, but it honestly seems to be acting like a char * that's only getting the first string. That's the data I can see in the watch).

Then it goes to the test assignment -- get this, it assigns it correctly and crashes. Not "after" -- somewhere during the "std::stirng test = MyString[1]" assignment. A watch shows "test" is assigned the correct string, but I have a memory access violation -- one I can't trace other than looking at the assembly code.

I'm still thinking I'm doing something idiotic, because I spent most of my day in meetings and then about three hours dicking around with wxStrings (which are an object) only to find that it was having both an internal problem (wxString didn't like the non-unicode literal I was passing it) AND crashing at the end of the brackets on the assignment.

I've looked at the intermediate files -- it seems fine (although those are such a ratty mess in places). It doesn't seem to be a scope problem. It's like it's got pointers but no space.

It seems to be unrelated to the extern -- apparently I'm just doing something stupid and the array is never getting space allocated or never correctly aiming the pointer. I'm considering reducing it to an array of char *'s just to see if that fixes it.

I think my biggest problem is simply I don't code this way. I would never, in a million years, have written something this way so it's really likely I'm doing something boneheaded and not realizing it. I'd have just built a class called "MyGlobalCrap" and created a single global copy, and had everyone use that. (Well, that's a lie -- this program is practically begging for a top-down OOD -- I'd have done it that way in the first place, and it'd be nice and clean. It was just written by someone who only half-understood C and didn't understand C++ using some third-party libraries that were in C++ and so half his examples were object oriented and his books weren't, hence the resulting mess. Which then evolved over 10 years.)

About the only bright side of my day is I worked out how to eek out more time on Borland Builder (we're up against some dumbass size issue that means it simply won't compile and link correctly if your project is over a certain size) -- we're using some static libraries I can change to dynamic ones and free up the space that way if I can't get this converted in time for the delivery.
ezrast
Terracotta Army
Posts: 2125


WWW
Reply #36 on: March 13, 2012, 06:36:36 PM

I don't think you can initialize arrays of non-primitive types that way. It sounds like the program is constructing the string objects and then unceremoniously dumping C-style character arrays into the memory areas of each one. Try replacing the declaration with

std::string myString[2];
myString[0] = "One";
myString[1] = "Two";
Morat20
Terracotta Army
Posts: 18529


Reply #37 on: March 13, 2012, 06:50:06 PM

Yeah, that tracks -- I think I tried that, and ran into basically a bunch of compiler bitching about how "MyString" was already definied -- it thought that:

std::string MyString[2];
MyString[0]="My String"

was actually creating another array (of size zero!) of Mystring.

I may just scream "fuck it all" and create an function that initializes every goddamn variable in the entire damn thing and sneak it into the constructor for the base class. I'd prefer to do it in the cpp, but beggars, choosers, etc.

This is what I get for not coding in C for a decade. I keep thinking in C# and Java terms.
Samwise
Moderator
Posts: 19324

sentient yeast infection


WWW
Reply #38 on: March 13, 2012, 07:10:23 PM

Yeah, you aren't allowed to do assignments outside of a function like that.  You declare your globals outside of a function but you can't put arbitrary code out there.  That would be madness.

I believe that if your global is an object, the object's constructor will be called (before main() I guess?), which is why I suggested using an object rather than an array.  But initializing stuff in a function that gets called before anything else is good too.
Morat20
Terracotta Army
Posts: 18529


Reply #39 on: March 13, 2012, 07:43:44 PM

Eh, I'll just sneak it into the common function .cpp, make sure it gets called first. Half the globals are pointers which don't need initialization other than being set to "null" (they're actually initalized to real values in the code, or else the entire mess would have exploded years ago) and what's not pointers are either primatives (a small handful, mostly stuff that should be consts but aren't) and strings used to populate various GUI menus and whatnot.

The whole thing's held together with spit and bailing wire, although at least now it's somewhat sanely organized.

The guy who has had the unenviable job of maintaining and adding to this rat's nest (who, in his defense, didn't design this mess and I've found his code segments to be generally very good, if obviously unfamiliar with OO principlies) would flip right the fuck out if he knew how much easier his job would be if he was allowed to shitcan the mess and rewrite it.

Especially if I could give him a quickie class in OO design and made him use C#. None of the GUI elements need anything exotic -- hell, even the plotting is done by gnu-plot and all the math is done by fortran. It only needs two coders (instead of just him) because the output from the engineers and demand for new features is just two much for one guy.

It physically hurts to look at some of the code and see how much easier, smaller, and more readable it'd be with a good design.

It's the part of the job I really, really hate -- seeing an old piece of code that's evolved, and really needs to be stripped and rebuilt to fit it's current needs and usage, which are considerably different from how it was originally designed. Except it's really hard to get management to sign off on "We'd like to, you know, have all your engineers stop making new shit we have to add and have the customers just take the next cycle off from expecting new features or better models. We'd like to rewrite it. So version 8.0 will look exactly like 7.0, but we'll be a lot happier with it from a working perspective".
Margalis
Terracotta Army
Posts: 12335


Reply #40 on: March 14, 2012, 04:22:04 AM

Allocating stuff in static code is something I always avoid because C++ static operation order is indeterminate and if you have your own memory manager or something (which you ALWAYS will on a console) you can allocate stuff before those are set up. Doing stuff in some actual method is almost always a better idea than trying to do it "before" the program starts.

vampirehipi23: I would enjoy a book written by a monkey and turned into a movie rather than this.
Quinton
Terracotta Army
Posts: 3332

is saving up his raid points for a fancy board title


Reply #41 on: March 14, 2012, 05:02:34 AM

A few random things to try.  I'm far more used to gcc/g++ on the commandline than dealing with VisualStudio, so these may not translate 100%, but here they are anyway;

- #including .cpp files is generally a bad habit -- your makefile or IDE project should list all the .cpp files
- turn on ALL the warnings (-Wall for commandline compilers) - C and C++ often assume you know what you're doing and will consider something that other languages would rate a fatal error to be only warning-worthy... and by default not all of these warnings are enabled.
- when you think your project/makefile may be causing headaches, try making clean (blowing away all intermediate and final output)
- be sure to include the correct include files for the classes you're using -- #include <string> before using std::string, etc -- some compilers will let you get away with not including the necessary headers but end up generating incorrect code as a result
- Margalis is right -- static initializers in C++ are dangerous -- by spec they happen in an indeterminate order -- if you have global state, have an init() or some init_foo()/init_bar()/etc functions you call before doing anything else that sets it up sanely.

I tried reproducing your issue with a little test program, but couldn't (but again, on Linux-x86-64 w/ g++...).  It may be possible that if your header refers to std::string foo[2]; but it's actually defined as std::string[] = { ... }; that you're running into a situation where some files think they're dealing pointers of different types.  I didn't think modern C++ had this issue, but it might not hurt to ensure that apart from the extern, the way you declare the globals in your header matches the way you declare them in globals.cpp exactly.
Lantyssa
Terracotta Army
Posts: 20848


Reply #42 on: March 14, 2012, 07:12:47 AM

Then it goes to the test assignment -- get this, it assigns it correctly and crashes. Not "after" -- somewhere during the "std::stirng test = MyString[1]" assignment. A watch shows "test" is assigned the correct string, but I have a memory access violation -- one I can't trace other than looking at the assembly code.
Sounds like a typical memory allocation error.

I'm not familiar enough with C++ to know if that line should work as-is or not, but you'd see that if your string wasn't null-terminated or if you had a pointer you were using to write somewhere you shouldn't.

Hahahaha!  I'm really good at this!
Morat20
Terracotta Army
Posts: 18529


Reply #43 on: March 14, 2012, 11:01:40 AM

Yeah, I'm going with allocating/initializing this stuff in a method or function. It'll just make my life easier. In deference to my colleague having to relearn where I put everything, I'll follow the same naming convention as the files...

Ugh. That's what I get for trying to minimize change as much as possible, rather than doing it the way that's right.

And yeah, you're right about C/C++ letting you shoot yourself in the ass. The only reason this code was such a mess was that Borland C++ considers "redefinition" and "reinitialization" of variables as a warning, not an error.
Miguel
Terracotta Army
Posts: 1298

कुशल


Reply #44 on: March 14, 2012, 04:42:42 PM

Some compilers have very odd scoping rules, especially if you mix in "extern "C" blocks.

Probably what is happening is of the following lines:

std::string MyString[2];
MyString[0]="My String"

The second line could be allocating an object MyString of a different type, assigning it to a name-mangled variable MyString, then discarding the object immediately (especially if the compiler cannot locate a reference within the current scope to a matching object).  The compiler may even be clever enough to completely remove the code as it has no WAR or WAW side effects.

You'd have to look at the assembly source in order to be sure.

“We have competent people thinking about this stuff. We’re not just making shit up.” -Neil deGrasse Tyson
Morat20
Terracotta Army
Posts: 18529


Reply #45 on: March 14, 2012, 05:17:23 PM

Some compilers have very odd scoping rules, especially if you mix in "extern "C" blocks.

Probably what is happening is of the following lines:

std::string MyString[2];
MyString[0]="My String"

The second line could be allocating an object MyString of a different type, assigning it to a name-mangled variable MyString, then discarding the object immediately (especially if the compiler cannot locate a reference within the current scope to a matching object).  The compiler may even be clever enough to completely remove the code as it has no WAR or WAW side effects.

You'd have to look at the assembly source in order to be sure.
That fits with what I'm seeing. Initializing it in a function should fix that, since the compiler will throw a hissy if it can't find the matching global variable.

extern std::string MyString[2];

#include "globals.h"
void init() {
MyString[0] = "String 1"
MyString[1] = "String 2"
}

It'll either resolve that to the global or scream at the compiler stage, I won't have some weird run-time allocation nonsense.
« Last Edit: March 14, 2012, 05:19:10 PM by Morat20 »
Miguel
Terracotta Army
Posts: 1298

कुशल


Reply #46 on: March 15, 2012, 10:47:39 AM

Just to make you feel better (or worse, in this case), the following code compiles and runs fine on g++ version 4.4.5:

(Output of preprocessor with -E switch):

Code:

# 2 "main.cpp" 2


# 1 "globals.h" 1
extern std::string myString[2];
# 5 "main.cpp" 2
# 1 "globals.cpp" 1
std::string myString[] = {"One", "Two"};
std::string test = myString[1];
# 6 "main.cpp" 2

int main( void )
{
    std::cout << myString[0] << std::endl;
    myString[1] = "Reassign";
    std::cout << myString[1] << std::endl;
}
backend-new test # g++ -Wall -pedantic main.cpp -o main
backend-new test # ./main
One
Reassign
backend-new test #

My gut tells me you are getting into some weird late-binding problem specific to MSVC.

“We have competent people thinking about this stuff. We’re not just making shit up.” -Neil deGrasse Tyson
Morat20
Terracotta Army
Posts: 18529


Reply #47 on: March 15, 2012, 08:54:05 PM

Yeah. It appears to work fine for primatives (bools and ints, mostly) but not std::strings and wxStrings -- and the latter are objects.

Not arrays, not simple assignments. However, functionalizing it:

globals.cpp
wxString MyString[2];

void init() {
MyString[0] = "This";
MyString[1] = "That";
}

Appears to work fine -- at least it's compiling, linking, and moving further in the execute process. Sadly I had SUCH a mass of strings, arrays of strings, and the same of wxStrings that I spent most of my day today moving them to the relevant init() functions and still aren't done.

However, the fact that it resolves MyString and doesn't throw a link error or a compile error is heartening. It's scoping correctly -- it'd scream a hissy if it wasn't assigning to the globals (there are no other variables with that name in scope, and it's not creating multiple definitions either -- it screams about that too).

I'll be satisifed when it actually executes far enough to RUN the damn init() functions and assign them, but I think it'll work fine.

I spent part of today trying to work out how tough it would be to move the Borland Builder wxWidgets stuff from the static libraries to a dll, only to get frustrated at the bullshit that is Borland Builder. Making the dll was easy enough, getting the motherfucker to link to it has proven to be challenge.

Goddamn assert failure. We're moving to the new compiler because of this, and I was hoping by switching to dynamic linking rather than static linking I'd basically free up enough space for the compiler to hack this latest build, giving me another few months to finish this conversion -- rather then have everyone breathing down my neck about why it isn't finished.

It's not finished because (1) it's a giant motherfucking program, (2) It's not written Unicode-compliant, (3) It's not written to a sane ANSI standard and (4) People ignored 83 million warning messages that should have sent up screaming red flags that MSVC won't ignore.

They're lucky I got an executable this fast, even if it crashes. I hadn't even glanced at the code before this got dumped in my lap, and until three weeks ago NO ONE was saying "Oh, and Morat, we can't do the delivery until you finish the conversion then port in all the new code your counterpart is writing as you do it".

OTOH, my boss has stopped asking for regular status meetings and seems to now be reasonably assured I am competent, productive, and not wasting time. I've been with the group maybe 6 months, and I've been doing this sort of impossible to benchmark/estimate completion time shit since I got here. Since I've finally moved to code the OTHER GUI guy knows intimately, and can discuss with him at length what I'm having to do and why, I'm guessing he can finally give my boss some actual feedback.

It's not the bad "I've stopped asking because I'm replacing him" type of stopped asking. It's the "I can actually tell he's doing stuff and I'm going to leave him alone to do it" sort.

I can't blame the guy -- it's hard to assess whether I'm doing the job well, or in a timely manner, or anything like that when no one can give you firm estimates of anything. Hell, I'm the only one there that really understands what "extern" means, or how a linker works. It's not like they're all old-school "I work from makefiles" types.

Besides, either I produce or don't. *shrug*. The alpha build is going to be buggy as all fuck though. I'll get them something executable in time, but it'll be until beta or later before I've ironed out 95% of the conversion bugs. Too many code paths, and too many changes to do otherwise.
Pages: 1 [2] Go Up Print 
f13.net  |  f13.net General Forums  |  General Discussion  |  Topic: C/C++ Question  
Jump to:  

Powered by SMF 1.1.10 | SMF © 2006-2009, Simple Machines LLC