2

I'm a beginner to C++ and am trying to compile and use the fmt library in a basic program, but I'm having a problem getting it working. The program, Tester.cpp, is simply as follows:

// #define FMT_HEADER_ONLY
// #include <C:\Program Files (x86)\FMT\include\fmt\format.h>
#include <fmt/format.h> 
#include <iostream>
    
int main() {
  std::cout << "C++ Code Executing!" << std::endl;
  int num = 7;
    
 std::cout << fmt::format("The answer is {}.", num) << std::endl;  
}

I have never really used cmake before, but have taken the following steps to download and compile the library so far:

git clone https://github.com/fmtlib/fmt.git
cd fmt
mkdir build
cd build
cmake ..
cmake --build . --config Release
cmake --install .

This stores a range of header files into this directory:

C:\Program Files (x86)\FMT\include\fmt

As per the commented lines in my program, if I instruct it to use header only and include a pathway directly to the header file it does seem to work, however I believe I should be able compile the fmt library in a way that I can link to it without using header only, ideally as a static library, (though as a beginner I may be wrong on this). In it's current format if I compile with clang++ in the following way:

clang++ -isystem "C:\Program Files (x86)\FMT\include" Tester.cpp -o Tester.exe

I get the following error:

Tester-333c8c.o : error LNK2019: unresolved external symbol "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl fmt::v8::vformat(class fmt::v8::basic_string_view<char>,class fmt::v8::basic_format_args<class fmt::v8::basic_format_context<class fmt::v8::appender,char> >)" (?vformat@v8@fmt@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V?$basic_string_view@D@12@V?$basic_format_args@V?$basic_format_context@Vappender@v8@fmt@@D@v8@fmt@@@12@@Z) referenced in function main
Tester.exe : fatal error LNK1120: 1 unresolved externals
clang++: error: linker command failed with exit code 1120 (use -v to see invocation)

I believe this is because the compiler can find the header files for compilation, but the linker has no definition/object file to link into an executable? This is the part I am trying to fix, but have been unable to do so.

As per the instructions for fmt 8.1.1 I have tried using cmake with -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE to create a static library, resulting in a fmt.lib file, but trying to link to that doesn't seem to help resolve my issues either. I've also tried adding an -L flag to different directories when compiling, but still get the error.

Question

Is someone able to point out where I'm going wrong in trying to compile and use this library for my program?

I am on a Windows system. I have been using clang and Neovim for code editing, though I do have Visual Studio installed as I believe clang relies on part of the instillation. I'm ideally looking to do this purely through command line tools if possible. As a beginner I'm also a bit shaky on how some parts of cpp, such as header files, are supposed to work, should it be relevant.

EDIT As per HolyBlackCat's comment on linking I have also tried the following:

clang++ -isystem "C:\Program Files (x86)\FMT\include" -LC:/FMT -lfmt Tester.cpp -o Tester.exe

with fmt.lib located in C:\FMT and get and error that starts like so:

fmt.lib(format.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MD_DynamicRelease' doesn't match value 'MT_StaticRelease' in Tester-6086ae.o
msvcprt.lib(MSVCP140.dll) : error LNK2005: "public: __cdecl std::_Lockit::_Lockit(int)" (??0_Lockit@std@@QEAA@H@Z) already defined in libcpmt.lib(xlock.obj)
...
...
Many lines...
...
LINK : warning LNK4217: symbol '_isatty' defined in 'libucrt.lib(isatty.obj)' is imported by 'fmt.lib(format.obj)' in function '"void __cdecl fmt::v8::detail::print(struct _iobuf *,class fmt::v8::basic_string_view<char>)" (?print@detail@v8@fmt@@YAXPEAU_iobuf@@V?$basic_string_view@D@23@@Z)'
Tester.exe : fatal error LNK1169: one or more multiply defined symbols found
clang++: error: linker command failed with exit code 1169 (use -v to see invocation)
Levenal
  • 3,796
  • 3
  • 24
  • 29
  • If you compile it to a static library then you likely need to link that static library. I say likely because msvc has a #pragma that can add linker settings. – drescherjm Mar 10 '22 at 17:52
  • *"trying to link to that doesn't seem to help"* How exactly did you link it? It should work. – HolyBlackCat Mar 10 '22 at 18:02
  • Unrelated to the question, consider using `CMAKE_INSTALL_PREFIX` to get a better installation directory; installing to `Program Files` is fairly unconventional. *"CMAKE_POSITION_INDEPENDENT_CODE=TRUE to create a static library"* That's not required to create a static library. You only need it if the static library will be linked into a shared library. *"Visual Studio installed as I believe clang relies on part of the instillation"* It can use either VS or MinGW, depending on which standard library you prefer. – HolyBlackCat Mar 10 '22 at 18:11
  • Please show the full error message – Alan Birtles Mar 10 '22 at 18:12
  • @HolyBlackCat I've tried a command like this : clang++ -isystem "C:\Program Files (x86)\FMT\include" -L "C:\FMT\fmt.lib" Tester.cpp -o Tester.exe – Levenal Mar 10 '22 at 18:13
  • @AlanBirtles Full error copied in – Levenal Mar 10 '22 at 18:14
  • That's not how you link a library. `-L` needs a directory (`-LC:/FMT`) You also need `-lfmt` (`.lib` extension will be added automatically). – HolyBlackCat Mar 10 '22 at 18:14
  • @HolyBlackCat Edited question to show results of a command based on your suggestion. – Levenal Mar 10 '22 at 18:32
  • `-l` flags must be after any source or object files, not before. But it seems it's not what causes the issue here. – HolyBlackCat Mar 10 '22 at 18:45
  • `clang`+MSVC is a weird combination, so I'm unsure how to handle this. It would be easier to use `clang`+MinGW (by replacing official Clang binaries with the ones from [MSYS2](https://stackoverflow.com/q/30069830/2752075)). OR use `clang-cl`+MSVC (a wrapper that accepts MSVC-flavored arguments) - then `/MD` flag should fix the issue (I'm not sure if there's an equivalent for the regular `clang`). The former option (clang+mingw), which is what I prefer, gives you the same standard library on all platforms. The latter gives you compatibility with VS (the IDE), if that's what you like. – HolyBlackCat Mar 10 '22 at 18:53
  • @HolyBlackCat Do you know what a MSVC/clang & MinGW command would look like? If my overall process is correct I can try to sort one of those environments to test. I think cmake used MSVC which may be causing an issue. – Levenal Mar 10 '22 at 20:01
  • For MinGW the command will be the same as what you already use, except I would use a custom `CMAKE_INSTALL_PREFIX` (or skip installation completely), since otherwise your installed libraries will be mixed with those installed by MSYS2, I think. As for `clang-cl`, it should accept the same flags as `cl` (MSVC compiler). But I've never used it from the command-line (only from VS, simply by flipping a bool in project settings). – HolyBlackCat Mar 10 '22 at 20:15
  • Additionally, MSYS2 has prebuilt libfmt in its repos. If you decide to install it, `pkg-config --cflags --libs fmt` will tell you the right flags to use it. – HolyBlackCat Mar 10 '22 at 20:25
  • 1
    You are approaching to this problem in a wrong way. Acquiring libraries is as easy as `vcpkg install fmt` nowadays. Same for consuming `target_link_libraries(MyApp PRIVATE fmt::fmt)` – Osyotr Mar 10 '22 at 20:26

2 Answers2

1

You should like with the {fmt} library as documented in https://fmt.dev/latest/usage.html. For example, if you are using CMake it can be done as follows:

target_link_libraries(<your-target> fmt::fmt)
vitaut
  • 49,672
  • 25
  • 199
  • 336
0

Posting my eventual solution/workaround,

Following my comment dialog with HolyBlackCat, I decided to remove MSVC and installed a MinGW-w64 environment (via Choclatey winlibs). I then built FMT via cmake as follows:

mkdir build
cd build
cmake .. -G "MinGW Makefiles"
cmake --build .

I was then able to compile my program in the following way:

clang++ Tester.cpp -o Tester.exe -isystem "C:\Coding\3rd_Party\C++\fmt\include" -L "C:\Coding\3rd_Party\C++\fmt\build" -l:libfmt.a

Which resulted in successful compilation and a a runnable executable.

I suspect the issue was that cmake was building using MSVC and then trying to link to that with clang was causing an incompatibility somewhere.

Levenal
  • 3,796
  • 3
  • 24
  • 29