2

I've compiled VolPack (https://graphics.stanford.edu/software/volpack/) as a static library in Visual Studio 6 (under Windows XP) because I think that's what it was made for and it wouldn't compile under Visual Studio 2019. But trying to write a C++ program in Visual Studio 2019 and link to volpack.lib I get the error: Error LNK1104 cannot open file 'LIBCD.lib'

(I don't think this is an error specific to VolPack, I think it applies to any lib compiled under VC 6 and then linked to in a later version of VS, so I think the question isn't too specific for StackOverflow.)

I found this: https://support.microsoft.com/en-us/help/154753/description-of-the-default-c-and-c-libraries-that-a-program-will-link, which explains why it's using that lib, but I don't know what to do about it in VS 6. I don't see an option regarding multithreading to change to make it use a different lib, and a quick Google search reveals that VS 6 doesn't support multithreading.

I did find this: How to solve "cannot open file 'LIBCD.lib' " in visual studio 2008?, but I'm not sure the solution is relevant to my issue because the string "GLUI" occurs nowhere in the lib I'm trying to link to. Even if it is the solution to my problem, I don't know where to get the source code for GLUI, exactly what changes I need to make to the makefile, or how to make VS 6 use the new recompiled GLUI, whatever that is.

The next solution was basically to tell the linker to ignore LIBCD.lib, but that gave me other errors. I found this: https://www.youtube.com/watch?v=zKrggjsgQx4, which basically says to ignore LIBCD.lib and then change the runtime library to multi-threaded debug, but that gave me errors too.

The errors I get when trying to compile VolPack under VS 2019 are all "potentially unitialized local pointer variable", so it's probably some simple modifications to get it to compile under VS 2019, but I'm not an expert at C and I don't want to deal with the headache of trying to figure out how to modify the program and of potentially breaking it. So f anybody knows an easy way to solve that problem that might work too. Thanks.

inhahe
  • 331
  • 1
  • 7
  • 1
    You write C but then you write C++. Then you write C again. Which one is it? – Asteroids With Wings Jun 10 '20 at 21:51
  • I don't think you will be able to get around this error. – drescherjm Jun 10 '20 at 21:56
  • Reminder: C== allows for function overloading. Many compiler vendors use *name mangling* to resolve overloaded functions. Many C libraries don't account for the name mangling. This is why mixing the two languages is not advised. – Thomas Matthews Jun 10 '20 at 21:56
  • 3
    It is unwise and invites trouble to try to use compiler suites of so greatly different generations to try to build one program. Instead of trying to make this odd mashup, I recommend focusing on getting everything to compile with the same version of VS -- presumably VS 2019. – John Bollinger Jun 10 '20 at 21:57
  • I agree, try to compile in VS 2019. This is a compiler warning C4703. You probably have the SDL option to treat all warnings as errors enabled, which is wise. From what I get, you need to initialize all unitialized local variables to nullptr or whatever more appropriate. From there on you're on your own. – Visar Jun 10 '20 at 22:05
  • 3
    VC++ 6 is completely dead. It doesn't even compile C++ as we know it, as it predates the original standard. – Asteroids With Wings Jun 10 '20 at 22:06
  • Consider using the [GCC](http://gcc.gnu.org/) C++ compiler, perhaps as [MinGW](http://mingw-w64.org/doku.php), or install a Linux distribution on your laptop (e.g. [Debian](http://debian.org/) or [Ubuntu](https://ubuntu.com/)...) after having backed up your important data. Most Linux systems are *very* developer friendly. Compile with `g++ -Wall -Wextra -g` (all warnings and debug info). Consider also [Clang](http://clang.llvm.org/)... – Basile Starynkevitch Jun 11 '20 at 06:27

2 Answers2

2

When you use a static library that has dependencies on other libraries then the compiler is going to complain if it can not find those other libraries.

The difference between Microsoft Visual Studio 6.x and Visual Studio 2019 is immense. As I've ported old code, both C and C++, from 6.x to VS 2015 I have had to make a number of source code changes to allow compiles to work due to the improvements of Microsoft in its adherence to the standards as well as supporting new versions of the standards, some of which deprecate previously supported constructs.

See this post about compatibility between versions, Library ABI compatibility between versions of Visual Studio See as well:

Binary compatibility between VS2017 and VS2015

ABI-Compatibility of visual studio c-libraries

And this blog posting from Microsoft about the release of Visual Studio 2019 dated 2019 says:

Visual Studio 2019 version 16.0 is now available and is binary compatible with VS 2015/2017. In this first release of VS 2019, we’ve implemented more compiler and library features from the C++20 Working Paper, implemented more overloads (C++17’s “final boss”), and fixed many correctness, performance, and throughput issues. Here’s a list of the C++17/20 compiler/library feature work and the library fixes. (As usual, many compiler bugs were also fixed, but they aren’t listed here; compiler fixes tend to be specific to certain arcane code patterns. We recently blogged about compiler optimization and build throughput improvements in VS 2019, and we maintain a documentation page about compiler conformance improvements in VS 2019.)

I don't see any other recourse than to work with the source to make it compatible with Visual Studio 2019. If you look, you may find that your level of expertise with C is sufficient for to implement some of the suggestions below.

The best course of action would seem to be to make the necessary source code changes to the library so that it will compile correctly under Visual Studio 2019.

The best course of action is to review the source code in each place where the uninitialized pointer error is being found and correct the source code so that the error is no longer generated. The programmers who previously worked in those areas of code obviously left out a potential flow of control probably due to their expectation that the missing code would never be executed based on the assumptions of how the function would be used. Most likely any such functions are complex and may need to be refactored.

Most often I have seen this type of missing flow being due to a switch statement missing a default: to capture the event of the switch variable not being one of the specified case values. It may also be due to an if statement that is a series of else if without a final else to capture any other possible condition. Or it may also be due to a loop which has a break statement before the pointer variable is initialized or which uses a continue statement to skip where the variable is initialized.

In most cases these issues are due to functions with low cohesion and/or are overly complex and large with maintenance actions over time introducing these kinds of problems.

A less desirable practice for the error of a potentially uninitialized pointer is to just initialize with some appropriate value. You can jump to where the error is, click on the variable and go to where it is defined, and then set it to an appropriate value. In most cases a value of NULL for pointers is safest because if it stays NULL and is used without being modified to a correct value, your application should crash letting you know there is a problem.

By initializing to NULL you are assuming the previous programmers knew what they were doing and the possible flow(s) detected by the compiler that would leave the variable unchanged to a proper value will never happen due to the logic.

And should the uninitialized pointer flow happen, you will find out when the application crashes. Unfortunately tracing back to the origin of the crash may be difficult.

You can use assert and other tests within the code to create a break point should you be debugging or to generate an exception, if it is C++, which may be more informative than just crashing. So adding such a test just before the line of source code generating the error, checking for NULL in the test before using the pointer may be helpful.

Or if the function has a status code indicating if it worked or not and any errors along with some way of returning an error status then the most efficient thing would be to use that error reporting at the pointer where the potentially uninitialized pointer error is being generated should the sanity check fail.

However if you can discern a safe default value, you may want to use that instead. Discerning a safe value will require reviewing the source code to determine what the safe default value should be. Sometimes such a safe value can be the address of a variable of the appropriate type initialized to zero.

Warning: if the address in the pointer is being returned, do not use the address of a variable local to the function. When the function returns that address will no longer be valid leading to Undefined Behavior.

Warning 2: if the address in the pointer is expected to have been allocated using malloc() or new or similar memory allocator then you must use the same mechanism so that when some code decides to deallocate the memory using free() or delete then it will work.

This review will require reading the source code of the function where the uninitialized pointer is being used if the value of the pointer is localized to the function itself. If the pointer is returned by the function, the value exported by the function to a user of the function, then you will also need to review the source code where the function is used in order to determine an appropriate default value.

So what I suggest you do is that every place where you do the initialization, you add a unique identifying comment (something along the lines of "Inhahe fix uninitialized pointer error 06/10/2020") to the line so that you can then do a search to find these at a later time and then go back into the problematic code and actually fix the compiler error by refactoring or changing the code to eliminate the possible uninitialized pointer error.

And before you do anything get the source under version control of some kind.

Richard Chambers
  • 16,643
  • 4
  • 81
  • 106
  • Thanks for your lengthy and informative response! Maybe this isn't ideal practice, but I ended up solving the problem by just compiling the lib in VS 2015 instead of VS 2019. I figured that if VS 6 doesn't give errors and VS 2019 does, there's probably an intermediate version that doesn't give errors but is modern enough to call the right libraries. I had been reluctant to test every version of VS to see which is the greatest one that doesn't give the errors, but then I just went for VS 2015 and it worked, and I'll leave it at that. I'll bookmark this response for future reference though. – inhahe Jun 11 '20 at 04:48
  • @inhahe that sounds like a good approach. I've updated my answer with some additional links about ABI compatibility between Visual Studio 2015/2017/2019 which indicates that a static library compiled with Visual Studio 2015 should be compatible with Visual Studio 2019 compiled code. My impression is that around VS 2015, Microsoft really got started on the static analysis part of their compiler. I know that the number of warnings that I saw from VS 2005 to VS 2013 only increased further with a move to VS 2015. Part of that was from the new security functions added to the standard library. – Richard Chambers Jun 11 '20 at 06:27
0

Although it would be wonderful if a X64 build could link with a old 32bit VS6.0 build, I think it will be a difficult path.

I believe the best hope is to take the WinXP code you have and retarget it for VS2019. I have had to do this a lot in the past few years.
There is a One Time Upgrade path where you just read in the VS6.0 workspace and it builds a new set of project files and SLN files.

My reading on this led me to believe the VS6->VSnewer was done back in VS2008 time frame, and there are some "breaking changes" in 'C++' and in the MSBUILD environment along the way, but you can survive it and get buildable code with minor changes.

One issue I found was with custom build steps. Any custom build steps with paths need trailing slashes for directories, and any custom build steps with quotes can be mangled causing your build to hang in VS2013/VS2019. (My work in this area was VS2013). The primary issue with the code is likely to be for loops old MS compiler allowed the scope of variables declared in a for loop to persist outside of the expected scope. There is a compiler switch to resolve that.

I recall that Platform Toolset V142 does not support XP systems, if you need to target a Win32 OS, then you have to use V141_xp for those binaries, otherwise the source code should build fine with the latest platform toolset V142 as a x64 build for modern windows. I have successfully migrated a TON of VS6.0 user programs to VS2013 and now VS2019 with few issues.

There is a WIN32 #define that is OK to leave it as it is legacy and does not break x64 builds.

Also in VS6.0 there is a rare compiler bug with "String Pooling" where the generated assembly will optimize away 2 different strings as though they were the same. String pooling is on with "Program Database For Execute And Continue" This triggers the string pooling option behind the scenes. So I always change this to "Program Database", as I never saw a bug fix for this, and suspect it may still be in the code. The bug was nasty enough and hard enough to find that I'm still gun shy years later.

Google Breaking Changes in C. But typically the modern compilers enforce things that were not even defined back in 1998. I have found many bugs now at compile time that were skipped/unnoticed in VS6.0.

I would take it one CPP module at a time, look at the first error and walk down. Then this nifty library you are using would have a new life.

Ross Youngblood
  • 502
  • 1
  • 3
  • 16