msvc how to handle declspecs

Hey guys,
I’m struggling to get msvc to work for my project.
I’m getting some undefined symbol linker errors for some const global data.

I’m using Cmake 3.27 with latest MSVC 17 and
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
However according to the docs this does not work for global data.

That’s why I did come up with

set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
project ("declspec")

set(LIB_NAME "otherLib")
set(LIB_SOURCES "otherLib/libTest.h" "otherLib/libTest.c")
add_library(${LIB_NAME} ${LIB_SOURCES})
if (MSVC)
   target_compile_definitions(${LIB_NAME}
    PRIVATE 
      DECL_SPEC_DATA=__declspec\(dllimport\)
      FOO_VAR=0
    PUBLIC
      DECL_SPEC_DATA=__declspec\(dllexport\)
      FOO_VAR=1
   )
endif()


set(EXE_NAME declspec)
add_executable (${EXE_NAME} "declspec.cpp" "declspec.h")
target_link_libraries(${EXE_NAME} PRIVATE ${LIB_NAME})

//mylib.h

  typedef struct _some_struct {
    int dummyInt;
    float dummyFloat;
  } some_struct_t;

  DECL_SPEC_DATA  extern const some_struct_t g_someStruct;

//my lib.c

const some_struct_t g_someStruct = {
  .dummyInt = 10,
  .dummyFloat = 10.0f
};

The Idea: For all sources compiled within the lib DECL_SPEC_DATA is set to import. For all sources from the exe DECL_SPEC_DATA is set to export.
However i still run into linker errors.
I did some digging:

after adding

#if (FOO_VAR != 0)
#error "FOO_VAR is not private"
#endif // (FOO_VAR != 0)

to libTest.c I’m getting a compiler error.

Looks like PUBLIC and PRIVATE definitions are getting messed up.

Can I not mix public and private in this way?
How would you handle the decl specs.Iis there any nicer way?

Thx for your help guys :slight_smile:

Haven’t read the details in your question. But there is a basic misunderstanding.
PUBLIC is the same as specifying PRIVATE and INTERFACE.
So you wanted to use INTERFACE instead of PUBLIC.

The PRIVATE, PUBLIC and INTERFACE keywords in target_link_libraries() don’t change the visibility of any symbols, at least not directly.

The CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS approach is at best a stop-gap measure, it shouldn’t be seen as a long term solution. I suggest you take a look at the GenerateExportHeader module as a much better way of handling which symbols you want to export and make visible. I won’t repeat all the details here, I’ll instead suggest you watch the first 13 minutes or so of my CppCon 2019 talk, which goes through this: https://youtu.be/m0DwB4OvDXk

Hey @jtxa , thx that did work in my particular case :slight_smile:

@craig.scott
Thx for this interesting input. This is a 3rd party, therefore I’d like to export everything ;).
So if i got this correctly generate_export_header() creates a new header which all the available functions. I can make cmake annotate all functions by using CMAKE_CXX_VISIBILITY_PRESET or CMAKE_VISIBILITY_INLINES_HIDDEN

Is this correct?

The CMAKE_CXX_VISIBILITY_PRESET and CMAKE_VISIBILITY_INLINES_HIDDEN variables are for setting the overall default visibility behavior, and normally you only set them when you want to hide things by default (already the default behavior with Visual Studio toolchains, but not the default with pretty much everything else). generate_export_header() is then used to provide a header that gives you macros you can use to annotate each thing you want to export. It isn’t for exporting all things in a blanket way.

ah thx for the clarification. I’ll have a look at that :slight_smile:

I believe the original CMake code does the opposite of what it should do. To clarify: if you compile the library you must export the data/functions, and if you compile the executable program you must import them. Together with the wrong PUBLIC (vs. INTERFACE) setting, the partially corrected code should IMHO read:

if (MSVC)
   target_compile_definitions(${LIB_NAME}
    PRIVATE 
      DECL_SPEC_DATA=__declspec\(dllexport\) # build setting for the library: export
      FOO_VAR=0
    INTERFACE
      DECL_SPEC_DATA=__declspec\(dllimport\) # for the consumer: import
      FOO_VAR=1
   )
endif()

That said, what Craig Scott said would be a better long term solution but I wanted to “fix” the IMHO wrong code anyway. Unless I am now mixing this up.