Using libcurl in a C++ program causes the Visual C++ compiler to emit errors

Viewed 1954

I have compiled libcurl on Windows using the Visual C++ compiler into a dll. In addition to the compiled libcurl source code, that dll contains a simple test program that looks as follows:

Header (HttpClient.h):

#pragma once

#include <string>

#include "curl/curl.h"


namespace My::Project
{
  class HttpClient
  {
  public:
    HttpClient();
    ~HttpClient();
    std::string Get(std::string address);

  private:
    CURL* easy_handle;
  };
}

Implementation (HttpClient.cpp):

#include "HttpClient.h"

#include <iostream>

using namespace My::Project


HttpClient::HttpClient()
{
  easy_handle = curl_easy_init();
  curl_global_init(CURL_GLOBAL_ALL);
}


HttpClient::~HttpClient()
{
  curl_global_cleanup();
}


static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
  ((std::string*)userp)->append((char*)contents, size * nmemb);
  return size * nmemb;
}


std::string HttpClient::Get(std::string address)
{
  std::string html;

  curl_easy_setopt(easy_handle, CURLOPT_URL, address.c_str());
  curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L);
  curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
  curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &html);
  CURLcode returnValue = curl_easy_perform(easy_handle);

  if(returnValue != CURLE_OK)
  {
    html = "Failed to retrieve HTML";
  }

  curl_easy_cleanup(easy_handle);
  return html;
}

That test program compiles just fine.

(BTW: I'm aware that curl_global_init and curl_global_cleanup must only be invoked once. It's just a test program...)

Now, I want to reference that dll in another dll and create an instance of the HttpClient class and use it as follows:

#include "<relative-path>/HttpClient.h" 

using namespace My::Project;

// Within a constructor of another class
HttpClient client;
std::string html = client.Get("https://www.google.com/");

The compiler call used to compile that second dll looks as follows:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\bin\HostX86\x64\CL.exe
/c
/I<project path>
/I"Generated Files\\"
/Iobj\Debug\x64\
/I<path to curl source>\curl\include
/ZI
/JMC
/ZW
/ZW:nostdlib
/nologo
/W3
/WX-
/diagnostics:column
/sdl
/MP
/Od
/Oy-
/D _WINRT_DLL
/D _WINDLL
/D _UNICODE
/D UNICODE
/D _DEBUG
/D WINAPI_FAMILY=WINAPI_FAMILY_APP
/D __WRL_NO_DEFAULT_LIB__
/Gm-
/EHsc
/RTC1
/MDd
/GS
/fp:precise
/Zc:wchar_t
/Zc:forScope
/Zc:inline
/std:c++17
/Yu"pch.h"
/Fp"obj\Debug\x64\pch.pch"
/Fo"obj\Debug\x64\\"
/Fd"obj\Debug\x64\vc142.pdb"
/Gd
/TP
/wd28204
/FU"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.21.27702\lib\x86\store\references\platform.winmd"
/FU"<other winmd files>
/analyze-
/FC
/errorReport:prompt
/bigobj
<path to cpp file invoking HttpClient>

However, attempting to compile the second dll causes the Visual C++ compiler to emit the following errors:

<path to curl source>\curl\include\curl\curl.h(134,29): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\curl.h(134,16): error C2146:  syntax error: missing ';' before identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(397,52): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(407,23): error C2079:  'curl_sockaddr::addr' uses undefined struct 'sockaddr'
<path to curl source>\curl\include\curl\curl.h(411,3): error C2065:  'curl_opensocket_callback': undeclared identifier
<path to curl source>\curl\include\curl\curl.h(411,27): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\curl.h(411,27): error C2378:  'curl_socket_t': redefinition; symbol cannot be overloaded with a typedef
<path to curl source>\curl\include\curl\curl.h(134): message :  see declaration of 'curl_socket_t'
<path to curl source>\curl\include\curl\curl.h(411,28): error C2513:  'int': no variable declared before '='
<path to curl source>\curl\include\curl\curl.h(411,28): error C2143:  syntax error: missing ';' before '('
<path to curl source>\curl\include\curl\curl.h(411,34): error C2062:  type 'void' unexpected
<path to curl source>\curl\include\curl\curl.h(416,59): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(113,19): error C3646:  'fd': unknown override specifier
<path to curl source>\curl\include\curl\multi.h(113,19): error C4430:  missing type specifier - int assumed. Note: C++ does not support default-int
<path to curl source>\curl\include\curl\multi.h(157,47): error C2061:  syntax error: identifier 'fd_set'
<path to curl source>\curl\include\curl\multi.h(271,51): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(292,76): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(296,62): error C2061:  syntax error: identifier 'curl_socket_t'
<path to curl source>\curl\include\curl\multi.h(410,55): error C2061:  syntax error: identifier 'curl_socket_t'

Following up with the first error message leads me to the following code fragment in curl.h:

131:  #ifndef curl_socket_typedef
132:  /* socket typedef */
133:  #if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
134:  typedef SOCKET curl_socket_t;
135:  #define CURL_SOCKET_BAD INVALID_SOCKET
136:  #else
137:  typedef int curl_socket_t;
138:  #define CURL_SOCKET_BAD -1
139:  #endif
140:  #define curl_socket_typedef
141:  #endif /* curl_socket_typedef */

So the compiler essentially complains about the following line:

134:  typedef SOCKET curl_socket_t;

However, I'm currently clueless at what exactly the problem is. The same code compiles fine with Clang for both Android and iOS.

Does anyone see what needs to be done to make this compile with Visual C++?

2 Answers

As was pointed out, SOCKET is defined by Windows. More specifically, it is defined in WinSock2.h. I included that particular header but without resolving the issues. However, the second dll utilizes a precompiled header file (pch.h). Once I included WinSock2.h in the pch.h file the compiler errors were finally resolved.

However, in order to build libcurl for Windows UWP apps there is another crucial step to be done. Including WinSock2.h in the pch.h file only resolved the compiler errors but led to two linker errors that look as follows:

error LNK2019: unresolved external symbol __imp_VerSetConditionMask referenced in function Curl_verify_windows_version
error LNK2019: unresolved external symbol __imp_VerifyVersionInfoA referenced in function Curl_verify_windows_version
fatal error LNK1120: 2 unresolved externals

These in terms can be resolved by instructing the linker to include the additional library OneCore.lib. Microsoft describes this library as follows:

For convenience, an umbrella library named OneCore.lib is provided in the Microsoft Windows Software Development Kit (SDK), which provides the exports for the subset of Win32 APIs that are common to all Windows 10 devices. Link your classic desktop app or driver with OneCore.lib (and no other libraries) to access these APIs. If you link your app or driver to OneCore.lib, and you only call Win32 APIs in that library, then your app or driver will load successfully on all Windows 10 devices.

See this link for more details about OneCore.lib.

Here's the code that will work. I did run it and fetched result also.

#include <iostream>
#include <stdlib.h>
#define CURL_STATICLIB
#include "curl\curl.h"
#include <string>

#pragma comment(lib, "crypt32")
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wldap32.lib")


class HttpClient
{
public:
    HttpClient();
    ~HttpClient();
    std::string Get(std::string address);

private:
    CURL* easy_handle;
};

HttpClient::HttpClient()
{
    easy_handle = curl_easy_init();
    curl_global_init(CURL_GLOBAL_ALL);
}

HttpClient::~HttpClient()
{
    curl_global_cleanup();
}

static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
{
    ((std::string*)userp)->append((char*)contents, size * nmemb);
    return size * nmemb;
}

std::string HttpClient::Get(std::string address)
{
    std::string html;

    curl_easy_setopt(easy_handle, CURLOPT_URL, address.c_str());
    curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, WriteCallback);
    curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, &html);
    CURLcode returnValue = curl_easy_perform(easy_handle);

    if (returnValue != CURLE_OK)
    {
        html = "Failed to retrieve HTML";
    }

    curl_easy_cleanup(easy_handle);
    return html;
}

int main()
{
    HttpClient client;
    std::string html = client.Get("https://www.google.com/");
    std::cout << html;
    std::cin.get();
    return 0;
}

Also make sure to add the following:

Linker > Additional Dependencies > Normaliz.lib

Hope this helps.

Related