Problem with char in oapiReadItem_string

Thunder Chicken

Resident Lua Script Rabble-Rouser
Donator
Joined
Mar 22, 2008
Messages
5,847
Reaction score
5,509
Points
188
Location
Massachusetts
I am working with the API read utilities. One thing is giving me a problem, the char statement in oapiReadItem_string :

Code:
char* mesh_name;

void Helicopter::clbkSetClassCaps (FILEHANDLE cfg)
{

    oapiReadItem_string (input_file, "MESH_NAME", mesh_name);

}
The variable in the api call is supposed to be char * (a pointer, right?) and the linker isn't liking the above. I'm not familiar completely familiar with C++ and I am wondering if this has something to do with the length of the string.

This is what I get during linking, I can't make heads or tails of it:

Code:
Linking...
   Creating library C:OrbiterModulesHelicopter.lib and object C:OrbiterModulesHelicopter.exp
Helicopter.obj : error LNK2019: unresolved external symbol "void * __cdecl operator new[](unsigned int,struct std::_DebugHeapTag_t const &,char *,int)" (??_U@YAPAXIABU_DebugHeapTag_t@std@@PADH@Z) referenced in function "public: char * __cdecl std::_DebugHeapAllocator<char>::allocate(unsigned int,void const *)" (?allocate@?@D@std@@QAAPADIPBX@Z)
Helicopter.obj : error LNK2019: unresolved external symbol "struct std::_DebugHeapTag_t const & __cdecl std::_DebugHeapTag_func(void)" (?_DebugHeapTag_func@std@@YAABU_DebugHeapTag_t@1@XZ) referenced in function "public: char * __cdecl std::_DebugHeapAllocator<char>::allocate(unsigned int,void const *)" (?allocate@?@D@std@@QAAPADIPBX@Z)
C:OrbiterModulesHelicopter.dll : fatal error LNK1120: 2 unresolved externals
Does anyone see where I am screwing up?


-----Posted Added-----


If I declare only 'char mesh_name', I get the following error:

Code:
Compiling...
Helicopter.cpp
c:orbiterorbitersdkmy add-onshelicopterhelicopter.cpp(101) : error C2664: 'oapiReadItem_string' : cannot convert parameter 3 from 'char' to 'char *'
        Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
Here is line 101 of my code:

Code:
    oapiReadItem_string (input_file, "MESH_NAME", mesh_name);
Any ideas, anyone?

I think the first version of the code above is correct (declaring mesh_name as a pointer variable). I'm not sure if this is some problem with my syntax, or if there is some issue with the general length of the character string.
 
Last edited:
I am working with the API read utilities. One thing is giving me a problem, the char statement in oapiReadItem_string :
<snip>

Shouldn't it be "cfg" instead of "input_file"? Just a guess though, havn't investigated further on...

regards,
Face
 
Based on the (admittedly cryptic) error message, the linker is complaining about another method (not clbkSetClassCaps or oapiReadItem_string); however, there is a second problem so let's take these two issues one-at-a-time:

Your first example will compile and link fine; however, it would CTD at runtime because mesh_name is a pointer that is a global variable that was never initialized. i.e.,

Code:
char* mesh_name;   // <<< the compiler intializes all global variables to null unless the code explicitly initializes them
 
void Helicopter::clbkSetClassCaps (FILEHANDLE cfg)
{
    oapiReadItem_string (input_file, "MESH_NAME", mesh_name);  // << mesh_name == NULL here, so it would CTD
}

What you want to do instead is initialize the pointer to reference a block of memory that will actually contain the characters read in from the config file. For example:

Code:
char mesh_name[256];  // << allocates 256 bytes of memory and sets mesh_name to point to it
 
void Helicopter::clbkSetClassCaps (FILEHANDLE cfg)
{
    oapiReadItem_string (input_file, "MESH_NAME", mesh_name);  // value is stored inside the 256-byte-long 'mesh_name' memory block.  
}

Of course, if MESH_NAME in the config file is longer than 255 characters (which would be 256 characters with the trailing zero terminator) you will overflow your variable and quite probably CTD. But it's highly unlikely that a mesh name will be that long, so it should be fine.

Regarding the second problem (the linker error message), it is complaining about a method that it cannot locate in the runtime libraries:

Code:
"void * __cdecl operator new[](unsigned int,struct std::_DebugHeapTag_t const &,char *,int)" (??_U@YAPAXIABU_DebugHeapTag_t@std@@PADH@Z) referenced in function "public: char * __cdecl std::_DebugHeapAllocator<char>::allocate(unsigned int,void const *)" (?allocate@?@D@std@@QAAPADIPBX@Z)

Deciphering that a bit, here is the signature of the method it can't find:

Code:
void * operator new[](unsigned int, const &, char *,int)

That's the method that handles 'new' operator in C++, which is of course included in the standard libraries. Are you linking with the '/nodefaultlib' linker option? More information here: http://forums.msdn.microsoft.com/en-US/vcgeneral/thread/a91783c5-64ee-490c-8683-f82cf47ad98c/
 
What you want to do instead is initialize the pointer to reference a block of memory that will actually contain the characters read in from the config file. For example:

Code:
char mesh_name[256];  // << allocates 256 bytes of memory and sets mesh_name to point to it
 
void Helicopter::clbkSetClassCaps (FILEHANDLE cfg)
{
    oapiReadItem_string (input_file, "MESH_NAME", mesh_name);  // value is stored inside the 256-byte-long 'mesh_name' memory block.  
}
Of course, if MESH_NAME in the config file is longer than 255 characters (which would be 256 characters with the trailing zero terminator) you will overflow your variable and quite probably CTD. But it's highly unlikely that a mesh name will be that long, so it should be fine.

Thanks for the explanation, this makes sense. Is there any issue regarding the number of characters (if mesh_name from the config file is 23 characters, will the variable mesh_name have 23 characters and spaces). I know Fortran used to have fits if you tried to read a character string that wasn't the right length.

Regarding the second problem (the linker error message), it is complaining about a method that it cannot locate in the runtime libraries:

Code:
"void * __cdecl operator new[](unsigned int,struct std::_DebugHeapTag_t const &,char *,int)" (??_U@YAPAXIABU_DebugHeapTag_t@std@@PADH@Z) referenced in function "public: char * __cdecl std::_DebugHeapAllocator<char>::allocate(unsigned int,void const *)" (?allocate@?@D@std@@QAAPADIPBX@Z)
Deciphering that a bit, here is the signature of the method it can't find:

Code:
void * operator new[](unsigned int, const &, char *,int)
That's the method that handles 'new' operator in C++, which is of course included in the standard libraries. Are you linking with the '/nodefaultlib' linker option? More information here: http://forums.msdn.microsoft.com/en-US/vcgeneral/thread/a91783c5-64ee-490c-8683-f82cf47ad98c/

Here are my linker options:

/OUT:"C:\Orbiter\Modules\Helicopter.dll" /INCREMENTAL /NOLOGO /LIBPATH:"C:\Program Files\Microsoft Visual Studio 9.0\VC\lib" /LIBPATH:"C:\Program Files\Microsoft Platform SDK for Windows Server 2003 R2\Lib" /LIBPATH:"C:\Orbiter\Orbitersdk\lib" /DLL /MANIFEST /MANIFESTFILE:"Debug\Helicopter.dll.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /NODEFAULTLIB:"libc.lib" /NODEFAULTLIB:"libcd.lib" /NODEFAULTLIB:"libcpmtd.lib" /NODEFAULTLIB:"msvcrt.lib" /NODEFAULTLIB:"msvcirt.lib" /NODEFAULTLIB:"msvcrtd.lib" /DEBUG /PDB:"C:\Orbiter\Modules\Helicopter.pdb" /SUBSYSTEM:WINDOWS /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:PROMPT kernel32.lib user32.lib gdi32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib

The libraries referred to under the /NODEFAULTLIB options were based on setting up Visual C++ 2008 Express for making dlls in Orbiter (someone posted a how-to which I followed, can't remember exactly who or where).


-----Posted Added-----


Found this:

http://msdn.microsoft.com/en-us/library/799kze2z(VS.80).aspx

The first example seems to be exactly my situation with the char statement. Does anyone see how I can resolve it?
 
The error message is a standard "unresolved external symbol" message, which means that the linker cannot not locate a method that it needs. The root of the problem is that libc.lib and libcd.lib are excluded via 'nodefaultlib' switches, and so the link fails. In other words, you need libc.lib (for release builds) or libcd.lib (for debug builds) in order to link your DLL.

Try this:

1. Go into project properties -> Linker -> Input.
2. Look at the "Ignore all default libraries" setting; it should be set to No.
3. Now look at the "Ignore specific library" setting right below it. Remove libc and libcd from it.
4. Click OK.

Then try relinking and see how it works. Note that I'm using VS 2005 here, so your properties page may be slightly different; however, what you want to do is to not exclude libc.lib and libcd.lib from the link.

FWIW, here are the "Ignore specific library" settings I use for my vessels on VS 2005:

Debug: msvcrt;msvcirt;libci;libc;libcmt
Release: msvcrt;msvicrt;libci;libc
 
Is there any issue regarding the number of characters (if mesh_name from the config file is 23 characters, will the variable mesh_name have 23 characters and spaces). I know Fortran used to have fits if you tried to read a character string that wasn't the right length.
Wow, a while since I have programmed in Fortran ;)

No, there is no problem provided the string you want to store in mesh_name is shorter than the length of the mesh_name array. This is because C/C++ terminate the string by placing a null character at the end.

So, the following code:
Code:
char mesh_name[10];
mesh_name="Hello";
Yields an array with the letters H-e-l-l-o in the first five elements and the null character in the sixth element. Any function that takes mesh_name as an input will parse the array and use the null character to find when it has reached the end of the string.

See also:
http://en.wikipedia.org/wiki/C_string
http://en.wikipedia.org/wiki/Null_character

The first example seems to be exactly my situation with the char statement. Does anyone see how I can resolve it?
No, it is not the same situation. The "extern" keyword in the examples you presented tell the linker that those variables are declared in another file. You are declaring mesh_name globally in the same file as the rest of your code.

On a side note, you should avoid declaring global variables (mesh_name in this case) and try to keep them as members of the vessel class so that other instances of the vessel cannot modify them.


-----Posted Added-----


FWIW, here are the "Ignore specific library" settings I use for my vessels on VS 2005:

Debug: msvcrt;msvcirt;libci;libc;libcmt
Release: msvcrt;msvicrt;libci;libc
Interesting. I usually only have msvcrt and msvcirt in there. What is to be gained by ignoring libci;libc;libcmt and should I be doing that as a general rule for Orbiter projects?
 
On a side note, you should avoid declaring global variables (mesh_name in this case) and try to keep them as members of the vessel class so that other instances of the vessel cannot modify them.

My bad, these variables are members of the vessel class, I just clipped them out of the code without including that information.


-----Posted Added-----


Try this:

1. Go into project properties -> Linker -> Input.
2. Look at the "Ignore all default libraries" setting; it should be set to No.
3. Now look at the "Ignore specific library" setting right below it. Remove libc and libcd from it.
4. Click OK.

Did this, didn't make any difference.

FWIW, here are the "Ignore specific library" settings I use for my vessels on VS 2005:

Debug: msvcrt;msvcirt;libci;libc;libcmt
Release: msvcrt;msvicrt;libci;libc

I seem to have a problem with msvcirt.lib - if I remove it from the ignore list, the linker errors above dissappear, but then I get this:

Code:
LINK : fatal error LNK1104: cannot open file 'msvcirt.lib'
Build log was saved at "file://c:OrbiterOrbitersdkMy Add-OnsHelicopterDebugBuildLog.htm"
Helicopter - 1 error(s), 1 warning(s)
 
It is possible the libraries have different names in VS 2008 Express than VS 2005. You could try experimenting with excluding/not excluding different libraries. Given that the linker cannot find the method for new, it looks very much like an excluded library problem.
 
It is possible the libraries have different names in VS 2008 Express than VS 2005. You could try experimenting with excluding/not excluding different libraries. Given that the linker cannot find the method for new, it looks very much like an excluded library problem.

Does anyone know if there is a master list of methods and associated libraries? Just randomly experimenting with libraries (especially given my level of understanding of VC++) doesn't seem like the best course of action, especially since I am at the edge of my patience threshold.


-----Posted Added-----


FWIW, here are the "Ignore specific library" settings I use for my vessels on VS 2005:

Debug: msvcrt;msvcirt;libci;libc;libcmt
Release: msvcrt;msvicrt;libci;libc

Ummm...I think I found the problem. I was attempting to compile and link under 'debug' instead of 'release'. :dry: I copied the project from a pre-existing project and did not think to check this. All is well, for now, anyway.

Thanks to all
 
I'm glad it's working in a release build now. :) Note, however, that you will need a debug version if you want to use the Visual Studio debugger to step through the code. Also, when you run a debug version the runtime libraries perform additional checks on your in-memory variables to make sure they aren't overflowing their boundaries, so it is a good idea to build and test debug versions to detect bugs during development and then do a release build for final testing and release. Release builds are optimized for speed (which is what you want for a public release), but Debug builds are not optimized (so the debugger can locate variables correctly) and they run slower because of the additional variable validation checks as well.

If it works for a release build but fails with a debug build there must be some responsible setting that is different between the two configurations. Tracking down the problem can be tricky, however.
 
Back
Top