• Advertisement
Sign in to follow this  

Need help linking c++ program

This topic is 3764 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everyone. Im writing a small Tetris clone in c++, and would like some advice on how to go about creating (or actually linking) my application. First of all, im writing a stand-alone tetris api, and compiling it in a separate .lib project. That way I can create several testing projects (I'm a organization freak!), and maintain a nice structure/ good overview. However I've encountered a problem that id like some help with. So far I've created a base-class "Block", that Im using to derive the different types of tetris blocks from. Question 1: Is it possible to place all these derived class declarations in one single header file, to avoid having tonnes of files for a relatively simple architecture, without getting into trouble further down the road from a linking perspective? Example: ---block.h--- class Block {...}; ---block.h end--- ---block.cpp--- Block::Block(...) {...}... ---block.cpp end--- ---blocks.h--- class BlockA{...}; class BlockB{...}; ... class Block?{...}; ---blocks.h end--- (Definitions of above class in each their own separate file). Question 2: I'd like to be able to print various debug info from several member functions. This means I somehow need to make the individual Block derived classes aware of <iostream> (I like cout). How do I do this in a clever way without getting tonnes of linker errors from multiple definitions? If you need any additional specifications please let me know. Any help will be greatly appreciated!

Share this post


Link to post
Share on other sites
Advertisement
Hi,

You can put as many classes and declarations in header files as you want. Before your compiler compiles the code, all the text of header files is literally included in cpp files where you place your #include statements. The compiler doesn't know the difference between cpp and .h files, it just compiles all the cpp files that have all the .h text formatted in them.

The compiler compiles all cpp files in different object files (one for each cpp file), and then the linker links all that together.

Always use include guards for your header files.

If you put *definitions* in header files, you can get a linker error: if two different cpp files include that header file, the function definition will be compiled twice, and that gives a linker error because the linker will find twice a definition of the same function.

However if you put only declarations in header files and the definitions in the C++ files, it's fine.

So, you put the bodies of your class and the declarations of the functions of this class in the header, but you put the actual implementation of those functions in the .cpp files.

Please be sane, and put the implementation of your functions in cpp files that have a name corresponding to the header file where the class body is, so that people (including yourself) can find it back in a logical place.

About cout: you can #include <iostream> in as many cpp and .h files as you want. It won't give linker errors, because <iostream> contains declarations, not definitions.

Knowing how the compiler and linker steps work makes a lot clear about what to put where.

Share this post


Link to post
Share on other sites
If I didnt realise that insight is key, I likely wouldnt be here asking for help right now =) I prefer reading forums - hate writing in them, as you often get shall we say less desireable answers, leaving you with a feeling that you should be ashamed putting your stupidity on public display.

In regards to your cout explanation at the end - wont the compiler complain about the same functions being declared multiple times?

Reason im abandoning my usual silent way of solving my problems is that Ive been getting some linking errors ive been unable to decipher. I thought they might be related to iostream (first entry in log below), but probably not then.

In the hope that someone might be able to interpret it for me, ill paste my build output below:

------ Build started: Project: APITest, Configuration: Debug Win32 ------
Compiling...
stdafx.cpp
Compiling...
APITest.cpp
Compiling manifest to resources...
Linking...
msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "public: class std::basic_ostream<char,struct std::char_traits<char> > & __thiscall std::basic_ostream<char,struct std::char_traits<char> >::operator<<(class std::basic_ostream<char,struct std::char_traits<char> > & (__cdecl*)(class std::basic_ostream<char,struct std::char_traits<char> > &))" (??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z) already defined in TetrisAPI.lib(Block.obj)
msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl std::endl(class std::basic_ostream<char,struct std::char_traits<char> > &)" (?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z) already defined in TetrisAPI.lib(Block.obj)
libcpmt.lib(locale0.obj) : error LNK2005: "private: static class std::locale::_Locimp * __cdecl std::locale::_Getgloballocale(void)" (?_Getgloballocale@locale@std@@CAPAV_Locimp@12@XZ) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(locale0.obj) : error LNK2005: "private: static void __cdecl std::locale::facet::facet_Register(class std::locale::facet *)" (?facet_Register@facet@locale@std@@CAXPAV123@@Z) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(locale0.obj) : error LNK2005: "public: static void __cdecl std::_Locinfo::_Locinfo_dtor(class std::_Locinfo *)" (?_Locinfo_dtor@_Locinfo@std@@SAXPAV12@@Z) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(locale0.obj) : error LNK2005: "public: static void __cdecl std::_Locinfo::_Locinfo_ctor(class std::_Locinfo *,char const *)" (?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@PBD@Z) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(xmutex.obj) : error LNK2005: "public: void __thiscall std::_Mutex::_Lock(void)" (?_Lock@_Mutex@std@@QAEXXZ) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(xmutex.obj) : error LNK2005: "public: void __thiscall std::_Mutex::_Unlock(void)" (?_Unlock@_Mutex@std@@QAEXXZ) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(xlock.obj) : error LNK2005: "public: __thiscall std::_Lockit::_Lockit(int)" (??0_Lockit@std@@QAE@H@Z) already defined in msvcprtd.lib(MSVCP80D.dll)
libcpmt.lib(xlock.obj) : error LNK2005: "public: __thiscall std::_Lockit::~_Lockit(void)" (??1_Lockit@std@@QAE@XZ) already defined in msvcprtd.lib(MSVCP80D.dll)
LIBCMT.lib(invarg.obj) : error LNK2005: __invoke_watson already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(setlocal.obj) : error LNK2005: __configthreadlocale already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(lconv.obj) : error LNK2005: _localeconv already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(tidtable.obj) : error LNK2005: __encode_pointer already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(tidtable.obj) : error LNK2005: __decode_pointer already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0dat.obj) : error LNK2005: __amsg_exit already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0dat.obj) : error LNK2005: __initterm_e already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0dat.obj) : error LNK2005: _exit already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0dat.obj) : error LNK2005: __exit already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0dat.obj) : error LNK2005: __cexit already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(mlock.obj) : error LNK2005: __unlock already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(mlock.obj) : error LNK2005: __lock already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(winxfltr.obj) : error LNK2005: __XcptFilter already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(crt0init.obj) : error LNK2005: ___xi_a already defined in MSVCRTD.lib(cinitexe.obj)
LIBCMT.lib(crt0init.obj) : error LNK2005: ___xi_z already defined in MSVCRTD.lib(cinitexe.obj)
LIBCMT.lib(crt0init.obj) : error LNK2005: ___xc_a already defined in MSVCRTD.lib(cinitexe.obj)
LIBCMT.lib(crt0init.obj) : error LNK2005: ___xc_z already defined in MSVCRTD.lib(cinitexe.obj)
LIBCMT.lib(hooks.obj) : error LNK2005: "void __cdecl terminate(void)" (?terminate@@YAXXZ) already defined in MSVCRTD.lib(MSVCR80D.dll)
LIBCMT.lib(errmode.obj) : error LNK2005: ___set_app_type already defined in MSVCRTD.lib(MSVCR80D.dll)
LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library
LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
D:\Dev\Cpp\Projects\Tetris\Debug\APITest.exe : fatal error LNK1120: 1 unresolved externals
Build log was saved at "file://d:\Dev\Cpp\Projects\Tetris\APITest\Debug\BuildLog.htm"
APITest - 31 error(s), 2 warning(s)
========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========

Share this post


Link to post
Share on other sites
Quote:
Original post by sablaptop
In regards to your cout explanation at the end - wont the compiler complain about the same functions being declared multiple times?


It will if you don't use include guards. However by using include guards, the contents of a header file is only parsed once per .cpp file, and the compiler won't give errors.

Include guards work like this. For example in a header file "main.h", you could put:

#ifndef MAIN_H_INCLUDED
#define MAIN_H_INCLUDED

//everything of your header file here

#endif

Just make sure you use a unique name for the include guard in each header file and that it can't conflict with any other identifiers.

In some compilers, you can also just put "#pragma once" at the top of the header file, but that is less portable.

About your build errors: they have to do with the linker settings of your project, not your C++ code.

It looks like there are two kinds of problems: things it doesn't find, and things it finds twice...
Make sure that in the linker settings of your project, all the libraries you need are included, and that no libraries are included twice. Make sure you don't link against two different libraries that define the same things...

Try to follow these warnings:

LINK : warning LNK4098: defaultlib 'MSVCRTD' conflicts with use of other libs; use /NODEFAULTLIB:library
LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library

I'm not familiar with those libraries so I can't see the exact problem. It could as well be some crazy unexpected Visual Studio setting that causes this.

Share this post


Link to post
Share on other sites
I'm actually just using a standard console project, linking my TetrisAPI.lib.
I probably also need to consider a better way of expressing myself so I can explain why I didnt think include guards would solve my original question no. 1.
Ill take a look after work tomorrow and post an update. Thanks for the help.

Share this post


Link to post
Share on other sites
MSVCP80D.dll and the like and their associated libraries are dlls that are not optional, programs compiled with visual studio need them them, even just console programs.

It's not really a C++ problem. The problem looks like it is somewhere with the Visual Studio specific libraries like this MSVCP80D, not in your tetris library. Are all the linker settings of your both projects correct?

Share this post


Link to post
Share on other sites
libcmt.lib and msvcrt.lib are c runtime libraries. One of them should be ignored by the linker. Open up your project properties, go to linker->input and type in libcmtd.lib in "Ignore Specific Library". When building the release build you should type in libcmt.lib. The last d before .lib means that it is a debug version of that library.

Share this post


Link to post
Share on other sites
Yikes misinformation.


Visual Studio offers 4 different runtimes. Static and dynamic, debug and release. You can choose which one you want to link with. In this case you are linking both TetrisApi.lib and your main application with a visual studio runtime. This is fine. However, they are not linked to the SAME runtime so Visual Studio sees two different implementations for the same function and complains. Please don't ignore default lib unless you have to - it's masking the real problem. Just make sure that TetrisApi.lib and your application link with the same version of the Visual Studio runtime and it will all work fine.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement