add support for building windows DLLs - resolves #582

Mon, 10 Feb 2025 20:59:02 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 10 Feb 2025 20:59:02 +0100
changeset 1180
4c3a69b9723a
parent 1179
ca4c6f590a08
child 1181
183bf43aa9b9

add support for building windows DLLs - resolves #582

msvc/libucx_dll/libucx_dll.vcxproj file | annotate | diff | comparison | revisions
msvc/libucx_dll/libucx_dll.vcxproj.filters file | annotate | diff | comparison | revisions
msvc/ucx.sln file | annotate | diff | comparison | revisions
msvc/ucxtest_dll/ucxtest_dll.vcxproj file | annotate | diff | comparison | revisions
msvc/ucxtest_dll/ucxtest_dll.vcxproj.filters file | annotate | diff | comparison | revisions
src/allocator.c file | annotate | diff | comparison | revisions
src/cx/allocator.h file | annotate | diff | comparison | revisions
src/cx/array_list.h file | annotate | diff | comparison | revisions
src/cx/buffer.h file | annotate | diff | comparison | revisions
src/cx/common.h file | annotate | diff | comparison | revisions
src/cx/compare.h file | annotate | diff | comparison | revisions
src/cx/hash_key.h file | annotate | diff | comparison | revisions
src/cx/hash_map.h file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/json.h file | annotate | diff | comparison | revisions
src/cx/linked_list.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/cx/mempool.h file | annotate | diff | comparison | revisions
src/cx/printf.h file | annotate | diff | comparison | revisions
src/cx/properties.h file | annotate | diff | comparison | revisions
src/cx/streams.h file | annotate | diff | comparison | revisions
src/cx/string.h file | annotate | diff | comparison | revisions
src/cx/tree.h file | annotate | diff | comparison | revisions
src/properties.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/msvc/libucx_dll/libucx_dll.vcxproj	Mon Feb 10 20:59:02 2025 +0100
@@ -0,0 +1,254 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\cx\allocator.h" />
+    <ClInclude Include="..\..\src\cx\array_list.h" />
+    <ClInclude Include="..\..\src\cx\buffer.h" />
+    <ClInclude Include="..\..\src\cx\collection.h" />
+    <ClInclude Include="..\..\src\cx\common.h" />
+    <ClInclude Include="..\..\src\cx\compare.h" />
+    <ClInclude Include="..\..\src\cx\hash_key.h" />
+    <ClInclude Include="..\..\src\cx\hash_map.h" />
+    <ClInclude Include="..\..\src\cx\iterator.h" />
+    <ClInclude Include="..\..\src\cx\json.h" />
+    <ClInclude Include="..\..\src\cx\linked_list.h" />
+    <ClInclude Include="..\..\src\cx\list.h" />
+    <ClInclude Include="..\..\src\cx\map.h" />
+    <ClInclude Include="..\..\src\cx\mempool.h" />
+    <ClInclude Include="..\..\src\cx\printf.h" />
+    <ClInclude Include="..\..\src\cx\properties.h" />
+    <ClInclude Include="..\..\src\cx\string.h" />
+    <ClInclude Include="..\..\src\cx\test.h" />
+    <ClInclude Include="..\..\src\cx\tree.h" />
+    <ClInclude Include="..\..\src\cx\streams.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\allocator.c" />
+    <ClCompile Include="..\..\src\array_list.c" />
+    <ClCompile Include="..\..\src\buffer.c" />
+    <ClCompile Include="..\..\src\compare.c" />
+    <ClCompile Include="..\..\src\hash_key.c" />
+    <ClCompile Include="..\..\src\hash_map.c" />
+    <ClCompile Include="..\..\src\iterator.c" />
+    <ClCompile Include="..\..\src\json.c" />
+    <ClCompile Include="..\..\src\linked_list.c" />
+    <ClCompile Include="..\..\src\list.c" />
+    <ClCompile Include="..\..\src\map.c" />
+    <ClCompile Include="..\..\src\mempool.c" />
+    <ClCompile Include="..\..\src\printf.c" />
+    <ClCompile Include="..\..\src\properties.c" />
+    <ClCompile Include="..\..\src\string.c" />
+    <ClCompile Include="..\..\src\tree.c" />
+    <ClCompile Include="..\..\src\streams.c" />
+    <ClCompile Include="..\..\src\szmul.c" />
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>17.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{f19429a5-fc21-4639-9d0a-231a6f19deb4}</ProjectGuid>
+    <RootNamespace>libucx_dll</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <TargetName>libucx</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <TargetName>libucx</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <TargetName>libucx</TargetName>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <TargetName>libucx</TargetName>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;CX_WINDLL_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <CompileAs>CompileAsC</CompileAs>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
+    </Link>
+    <Lib>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
+      <AdditionalLibraryDirectories>
+      </AdditionalLibraryDirectories>
+    </Lib>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;CX_WINDLL_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <CompileAs>CompileAsC</CompileAs>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
+    </Link>
+    <Lib>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
+      <AdditionalLibraryDirectories>
+      </AdditionalLibraryDirectories>
+    </Lib>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_LIB;CX_WINDLL_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <CompileAs>CompileAsC</CompileAs>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
+    </Link>
+    <Lib>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
+      <AdditionalLibraryDirectories>
+      </AdditionalLibraryDirectories>
+    </Lib>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_LIB;CX_WINDLL_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <PrecompiledHeader>NotUsing</PrecompiledHeader>
+      <PrecompiledHeaderFile>
+      </PrecompiledHeaderFile>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <CompileAs>CompileAsC</CompileAs>
+      <AdditionalIncludeDirectories>
+      </AdditionalIncludeDirectories>
+    </ClCompile>
+    <Link>
+      <SubSystem>
+      </SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <ImportLibrary>$(OutDir)$(TargetName).dll.lib</ImportLibrary>
+    </Link>
+    <Lib>
+      <AdditionalDependencies>
+      </AdditionalDependencies>
+      <AdditionalLibraryDirectories>
+      </AdditionalLibraryDirectories>
+    </Lib>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/msvc/libucx_dll/libucx_dll.vcxproj.filters	Mon Feb 10 20:59:02 2025 +0100
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Header">
+      <UniqueIdentifier>{5DFE835D-CE31-47E5-A53D-8FC2490212C9}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Source">
+      <UniqueIdentifier>{1C26B29C-B3F9-43DB-9AB1-8823FA5CF3C2}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\src\cx\allocator.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\array_list.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\buffer.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\collection.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\common.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\compare.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\hash_key.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\hash_map.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\iterator.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\json.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\linked_list.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\list.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\map.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\mempool.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\printf.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\properties.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\string.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\test.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\tree.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\src\cx\streams.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\allocator.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\array_list.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\buffer.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\compare.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\hash_key.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\hash_map.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\iterator.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\json.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\linked_list.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\list.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\map.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\mempool.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\printf.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\properties.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\string.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\tree.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\streams.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\src\szmul.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
--- a/msvc/ucx.sln	Mon Feb 10 21:30:51 2025 +0100
+++ b/msvc/ucx.sln	Mon Feb 10 20:59:02 2025 +0100
@@ -5,8 +5,12 @@
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libucx", "libucx\libucx.vcxproj", "{A7EE56A3-0BAD-4CAB-9354-7FAE2A65E276}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libucx_dll", "libucx_dll\libucx_dll.vcxproj", "{F19429A5-FC21-4639-9D0A-231A6F19DEB4}"
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ucxtest", "ucxtest\ucxtest.vcxproj", "{56337F37-5298-49C4-B4D4-B43C1B2900F0}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ucxtest_dll", "ucxtest_dll\ucxtest_dll.vcxproj", "{13520243-14C7-488E-9389-6CFD5432C54C}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|x64 = Debug|x64
@@ -23,6 +27,14 @@
 		{A7EE56A3-0BAD-4CAB-9354-7FAE2A65E276}.Release|x64.Build.0 = Release|x64
 		{A7EE56A3-0BAD-4CAB-9354-7FAE2A65E276}.Release|x86.ActiveCfg = Release|Win32
 		{A7EE56A3-0BAD-4CAB-9354-7FAE2A65E276}.Release|x86.Build.0 = Release|Win32
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Debug|x64.ActiveCfg = Debug|x64
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Debug|x64.Build.0 = Debug|x64
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Debug|x86.ActiveCfg = Debug|Win32
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Debug|x86.Build.0 = Debug|Win32
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Release|x64.ActiveCfg = Release|x64
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Release|x64.Build.0 = Release|x64
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Release|x86.ActiveCfg = Release|Win32
+		{F19429A5-FC21-4639-9D0A-231A6F19DEB4}.Release|x86.Build.0 = Release|Win32
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Debug|x64.ActiveCfg = Debug|x64
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Debug|x64.Build.0 = Debug|x64
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Debug|x86.ActiveCfg = Debug|Win32
@@ -31,6 +43,14 @@
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Release|x64.Build.0 = Release|x64
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Release|x86.ActiveCfg = Release|Win32
 		{56337F37-5298-49C4-B4D4-B43C1B2900F0}.Release|x86.Build.0 = Release|Win32
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Debug|x64.ActiveCfg = Debug|x64
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Debug|x64.Build.0 = Debug|x64
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Debug|x86.ActiveCfg = Debug|Win32
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Debug|x86.Build.0 = Debug|Win32
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Release|x64.ActiveCfg = Release|x64
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Release|x64.Build.0 = Release|x64
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Release|x86.ActiveCfg = Release|Win32
+		{13520243-14C7-488E-9389-6CFD5432C54C}.Release|x86.Build.0 = Release|Win32
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/msvc/ucxtest_dll/ucxtest_dll.vcxproj	Mon Feb 10 20:59:02 2025 +0100
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tests\test_allocator.c" />
+    <ClCompile Include="..\..\tests\test_buffer.c" />
+    <ClCompile Include="..\..\tests\test_compare.c" />
+    <ClCompile Include="..\..\tests\test_hash_key.c" />
+    <ClCompile Include="..\..\tests\test_hash_map.c" />
+    <ClCompile Include="..\..\tests\test_iterator.c" />
+    <ClCompile Include="..\..\tests\test_json.c" />
+    <ClCompile Include="..\..\tests\test_list.c" />
+    <ClCompile Include="..\..\tests\test_mempool.c" />
+    <ClCompile Include="..\..\tests\test_printf.c" />
+    <ClCompile Include="..\..\tests\test_properties.c" />
+    <ClCompile Include="..\..\tests\test_string.c" />
+    <ClCompile Include="..\..\tests\test_szmul.c" />
+    <ClCompile Include="..\..\tests\test_tree.c" />
+    <ClCompile Include="..\..\tests\test_streams.c" />
+    <ClCompile Include="..\..\tests\ucxtest.c" />
+    <ClCompile Include="..\..\tests\util_allocator.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tests\util_allocator.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\libucx_dll\libucx_dll.vcxproj">
+      <Project>{f19429a5-fc21-4639-9d0a-231a6f19deb4}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <VCProjectVersion>17.0</VCProjectVersion>
+    <Keyword>Win32Proj</Keyword>
+    <ProjectGuid>{13520243-14c7-488e-9389-6cfd5432c54c}</ProjectGuid>
+    <RootNamespace>ucxtest</RootNamespace>
+    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v143</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="Shared">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <OutDir>$(SolutionDir)..\build\msvc\$(Platform)\$(Configuration)\</OutDir>
+    <IntDir>build\$(Platform)\$(Configuration)\</IntDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CX_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\src\</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsC</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutputPath)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libucx.dll.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CX_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\src\</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsC</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutputPath)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libucx.dll.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CX_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\src\</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsC</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutputPath)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libucx.dll.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <SDLCheck>true</SDLCheck>
+      <PreprocessorDefinitions>NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;CX_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <ConformanceMode>true</ConformanceMode>
+      <LanguageStandard_C>stdc17</LanguageStandard_C>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\src\</AdditionalIncludeDirectories>
+      <CompileAs>CompileAsC</CompileAs>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalLibraryDirectories>$(OutputPath)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>libucx.dll.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/msvc/ucxtest_dll/ucxtest_dll.vcxproj.filters	Mon Feb 10 20:59:02 2025 +0100
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Header">
+      <UniqueIdentifier>{93481EC0-13B8-4FDD-877F-D9A4B4964A2F}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
+    </Filter>
+    <Filter Include="Source">
+      <UniqueIdentifier>{1EB2A087-F9E5-4DD7-8B0C-6916FDD1121F}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\tests\test_allocator.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_buffer.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_compare.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_hash_key.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_hash_map.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_iterator.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_json.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_list.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_mempool.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_printf.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_properties.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_string.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_szmul.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_tree.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\test_streams.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\ucxtest.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\tests\util_allocator.c">
+      <Filter>Source</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\tests\util_allocator.h">
+      <Filter>Header</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
--- a/src/allocator.c	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/allocator.c	Mon Feb 10 20:59:02 2025 +0100
@@ -71,7 +71,7 @@
         &cx_default_allocator_class,
         NULL
 };
-CxAllocator *cxDefaultAllocator = &cx_default_allocator;
+const CxAllocator * const cxDefaultAllocator = &cx_default_allocator;
 
 int cx_reallocate_(
         void **mem,
--- a/src/cx/allocator.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/allocator.h	Mon Feb 10 20:59:02 2025 +0100
@@ -100,7 +100,8 @@
 /**
  * A default allocator using standard library malloc() etc.
  */
-extern CxAllocator *cxDefaultAllocator;
+cx_attr_export
+extern const CxAllocator * const cxDefaultAllocator;
 
 /**
  * Function pointer type for destructor functions.
@@ -145,6 +146,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_reallocate_(
         void **mem,
         size_t n
@@ -169,6 +171,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_reallocatearray_(
         void **mem,
         size_t nmemb,
@@ -218,6 +221,7 @@
  * @param mem a pointer to the block to free
  */
 cx_attr_nonnull_arg(1)
+cx_attr_export
 void cxFree(
         const CxAllocator *allocator,
         void *mem
@@ -235,6 +239,7 @@
 cx_attr_malloc
 cx_attr_dealloc_ucx
 cx_attr_allocsize(2)
+cx_attr_export
 void *cxMalloc(
         const CxAllocator *allocator,
         size_t n
@@ -257,6 +262,7 @@
 cx_attr_nonnull_arg(1)
 cx_attr_dealloc_ucx
 cx_attr_allocsize(3)
+cx_attr_export
 void *cxRealloc(
         const CxAllocator *allocator,
         void *mem,
@@ -285,6 +291,7 @@
 cx_attr_nonnull_arg(1)
 cx_attr_dealloc_ucx
 cx_attr_allocsize(3, 4)
+cx_attr_export
 void *cxReallocArray(
         const CxAllocator *allocator,
         void *mem,
@@ -310,6 +317,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 int cxReallocate_(
         const CxAllocator *allocator,
         void **mem,
@@ -356,6 +364,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 int cxReallocateArray_(
         const CxAllocator *allocator,
         void **mem,
@@ -398,6 +407,7 @@
 cx_attr_malloc
 cx_attr_dealloc_ucx
 cx_attr_allocsize(2, 3)
+cx_attr_export
 void *cxCalloc(
         const CxAllocator *allocator,
         size_t nelem,
--- a/src/cx/array_list.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/array_list.h	Mon Feb 10 20:59:02 2025 +0100
@@ -47,6 +47,7 @@
  * The maximum item size in an array list that fits into stack buffer
  * when swapped.
  */
+cx_attr_export
 extern const unsigned cx_array_swap_sbo_size;
 
 /**
@@ -218,6 +219,7 @@
 /**
  * A default stdlib-based array reallocator.
  */
+cx_attr_export
 extern CxArrayReallocator *cx_array_default_reallocator;
 
 /**
@@ -238,6 +240,7 @@
  * on the stack or shall not reallocated in place
  * @return an array reallocator
  */
+cx_attr_export
 CxArrayReallocator cx_array_reallocator(
         const struct cx_allocator_s *allocator,
         const void *stackmem
@@ -274,6 +277,7 @@
  * @see cx_array_reallocator()
  */
 cx_attr_nonnull_arg(1, 2, 3)
+cx_attr_export
 int cx_array_reserve(
         void **array,
         void *size,
@@ -317,6 +321,7 @@
  * @see cx_array_reallocator()
  */
 cx_attr_nonnull_arg(1, 2, 3, 6)
+cx_attr_export
 int cx_array_copy(
         void **target,
         void *size,
@@ -475,6 +480,7 @@
  * @retval non-zero failure
  */
 cx_attr_nonnull_arg(1, 2, 3, 5)
+cx_attr_export
 int cx_array_insert_sorted(
         void **target,
         size_t *size,
@@ -601,6 +607,7 @@
  * @see cx_array_binary_search()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cx_array_binary_search_inf(
         const void *arr,
         size_t size,
@@ -626,6 +633,7 @@
  * @see cx_array_binary_search_sup()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cx_array_binary_search(
         const void *arr,
         size_t size,
@@ -657,6 +665,7 @@
  * @see cx_array_binary_search()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cx_array_binary_search_sup(
         const void *arr,
         size_t size,
@@ -674,6 +683,7 @@
  * @param idx2 index of second element
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_array_swap(
         void *arr,
         size_t elem_size,
@@ -700,6 +710,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxListFree, 1)
+cx_attr_export
 CxList *cxArrayListCreate(
         const CxAllocator *allocator,
         cx_compare_func comparator,
--- a/src/cx/buffer.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/buffer.h	Mon Feb 10 20:59:02 2025 +0100
@@ -227,6 +227,7 @@
  * @return zero on success, non-zero if a required allocation failed
  */
 cx_attr_nonnull_arg(1)
+cx_attr_export
 int cxBufferInit(
         CxBuffer *buffer,
         void *space,
@@ -250,6 +251,7 @@
  * @see cxBufferWrite()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferEnableFlushing(
     CxBuffer *buffer,
     CxBufferFlushConfig config
@@ -265,6 +267,7 @@
  * @see cxBufferInit()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxBufferDestroy(CxBuffer *buffer);
 
 /**
@@ -279,6 +282,7 @@
  * @param buffer the buffer to deallocate
  * @see cxBufferCreate()
  */
+cx_attr_export
 void cxBufferFree(CxBuffer *buffer);
 
 /**
@@ -308,6 +312,7 @@
 cx_attr_malloc
 cx_attr_dealloc(cxBufferFree, 1)
 cx_attr_nodiscard
+cx_attr_export
 CxBuffer *cxBufferCreate(
         void *space,
         size_t capacity,
@@ -352,6 +357,7 @@
  * @see cxBufferShiftRight()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferShift(
         CxBuffer *buffer,
         off_t shift
@@ -368,6 +374,7 @@
  * @see cxBufferShift()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferShiftRight(
         CxBuffer *buffer,
         size_t shift
@@ -384,6 +391,7 @@
  * @see cxBufferShift()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferShiftLeft(
         CxBuffer *buffer,
         size_t shift
@@ -411,6 +419,7 @@
  *
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferSeek(
         CxBuffer *buffer,
         off_t offset,
@@ -430,6 +439,7 @@
  * @see cxBufferReset()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxBufferClear(CxBuffer *buffer);
 
 /**
@@ -442,6 +452,7 @@
  * @see cxBufferClear()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxBufferReset(CxBuffer *buffer);
 
 /**
@@ -454,6 +465,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 bool cxBufferEof(const CxBuffer *buffer);
 
 
@@ -468,6 +480,7 @@
  * @retval non-zero on allocation failure
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferMinimumCapacity(
         CxBuffer *buffer,
         size_t capacity
@@ -515,6 +528,7 @@
  * @see cxBufferRead()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cxBufferWrite(
         const void *ptr,
         size_t size,
@@ -542,6 +556,7 @@
  * @see cxBufferRead()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cxBufferAppend(
         const void *ptr,
         size_t size,
@@ -603,6 +618,7 @@
  * @see cxBufferEnableFlushing()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cxBufferFlush(CxBuffer *buffer);
 
 /**
@@ -621,6 +637,7 @@
  * @see cxBufferAppend()
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cxBufferRead(
         void *ptr,
         size_t size,
@@ -648,6 +665,7 @@
  * @see cxBufferTerminate()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferPut(
         CxBuffer *buffer,
         int c
@@ -666,6 +684,7 @@
  * @return zero, if the terminator could be written, non-zero otherwise
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferTerminate(CxBuffer *buffer);
 
 /**
@@ -679,6 +698,7 @@
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(2)
+cx_attr_export
 size_t cxBufferPutString(
         CxBuffer *buffer,
         const char *str
@@ -693,6 +713,7 @@
  * @return the character or @c EOF, if the end of the buffer is reached
  */
 cx_attr_nonnull
+cx_attr_export
 int cxBufferGet(CxBuffer *buffer);
 
 #ifdef __cplusplus
--- a/src/cx/common.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/common.h	Mon Feb 10 20:59:02 2025 +0100
@@ -266,6 +266,24 @@
 
 #endif // __STDC_VERSION__
 
+
+// ---------------------------------------------------------------------------
+//       MSVC specifics
+// ---------------------------------------------------------------------------
+
+#ifdef _MSC_VER
+// fix missing _Thread_local support
+#define _Thread_local __declspec(thread)
+#endif // _MSC_VER
+
+#if defined(CX_WINDLL_EXPORT)
+#define cx_attr_export __declspec(dllexport)
+#elif defined(CX_WINDLL)
+#define cx_attr_export __declspec(dllimport)
+#else
+#define cx_attr_export
+#endif // CX_WINDLL / CX_WINDLL_EXPORT
+
 // ---------------------------------------------------------------------------
 //       Useful function pointers
 // ---------------------------------------------------------------------------
@@ -340,17 +358,9 @@
 #if __cplusplus
 extern "C"
 #endif
-int cx_szmul_impl(size_t a, size_t b, size_t *result);
+cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result);
 #endif // cx_szmul
 
 
-// ---------------------------------------------------------------------------
-//       Fixes for MSVC incompatibilities
-// ---------------------------------------------------------------------------
-
-#ifdef _MSC_VER
-// fix missing _Thread_local support
-#define _Thread_local __declspec(thread)
-#endif // _MSC_VER
 
 #endif // UCX_COMMON_H
--- a/src/cx/compare.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/compare.h	Mon Feb 10 20:59:02 2025 +0100
@@ -56,6 +56,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 typedef int (*cx_compare_func)(
     const void *left,
     const void *right
@@ -75,6 +76,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_int(const void *i1, const void *i2);
 
 /**
@@ -87,6 +89,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_int(int i1, int i2);
 
 /**
@@ -103,6 +106,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_longint(const void *i1, const void *i2);
 
 /**
@@ -115,6 +119,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_longint(long int i1, long int i2);
 
 /**
@@ -131,6 +136,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_longlong(const void *i1, const void *i2);
 
 /**
@@ -143,6 +149,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_longlong(long long int i1, long long int i2);
 
 /**
@@ -159,6 +166,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_int16(const void *i1, const void *i2);
 
 /**
@@ -171,6 +179,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_int16(int16_t i1, int16_t i2);
 
 /**
@@ -187,6 +196,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_int32(const void *i1, const void *i2);
 
 /**
@@ -199,6 +209,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_int32(int32_t i1, int32_t i2);
 
 /**
@@ -215,6 +226,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_int64(const void *i1, const void *i2);
 
 /**
@@ -227,6 +239,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_int64(int64_t i1, int64_t i2);
 
 /**
@@ -243,6 +256,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_uint(const void *i1, const void *i2);
 
 /**
@@ -255,6 +269,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_uint(unsigned int i1, unsigned int i2);
 
 /**
@@ -271,6 +286,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_ulongint(const void *i1, const void *i2);
 
 /**
@@ -283,6 +299,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2);
 
 /**
@@ -299,6 +316,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_ulonglong(const void *i1, const void *i2);
 
 /**
@@ -311,6 +329,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2);
 
 /**
@@ -327,6 +346,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_uint16(const void *i1, const void *i2);
 
 /**
@@ -339,6 +359,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_uint16(uint16_t i1, uint16_t i2);
 
 /**
@@ -355,6 +376,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_uint32(const void *i1, const void *i2);
 
 /**
@@ -367,6 +389,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_uint32(uint32_t i1, uint32_t i2);
 
 /**
@@ -383,6 +406,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_uint64(const void *i1, const void *i2);
 
 /**
@@ -395,6 +419,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_uint64(uint64_t i1, uint64_t i2);
 
 /**
@@ -411,6 +436,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_float(const void *f1, const void *f2);
 
 /**
@@ -423,6 +449,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_float(float f1, float f2);
 
 /**
@@ -439,6 +466,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_double(const void *d1, const void *d2);
 
 /**
@@ -451,6 +479,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_double(double d1, double d2);
 
 /**
@@ -467,6 +496,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_intptr(const void *ptr1, const void *ptr2);
 
 /**
@@ -479,6 +509,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2);
 
 /**
@@ -495,6 +526,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_uintptr(const void *ptr1, const void *ptr2);
 
 /**
@@ -507,6 +539,7 @@
  * @retval 1 if the left argument is greater than the right argument
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2);
 
 /**
@@ -520,6 +553,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cx_cmp_ptr(const void *ptr1, const void *ptr2);
 
 #ifdef __cplusplus
--- a/src/cx/hash_key.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/hash_key.h	Mon Feb 10 20:59:02 2025 +0100
@@ -76,6 +76,7 @@
  * @see cx_hash_key()
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_hash_murmur(CxHashKey *key);
 
 /**
@@ -88,6 +89,7 @@
  */
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
+cx_attr_export
 CxHashKey cx_hash_key_str(const char *str);
 
 /**
@@ -99,6 +101,7 @@
  */
 cx_attr_nodiscard
 cx_attr_access_r(1, 2)
+cx_attr_export
 CxHashKey cx_hash_key_bytes(
         const unsigned char *bytes,
         size_t len
@@ -117,6 +120,7 @@
  */
 cx_attr_nodiscard
 cx_attr_access_r(1, 2)
+cx_attr_export
 CxHashKey cx_hash_key(
         const void *obj,
         size_t len
--- a/src/cx/hash_map.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/hash_map.h	Mon Feb 10 20:59:02 2025 +0100
@@ -85,6 +85,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxMapFree, 1)
+cx_attr_export
 CxMap *cxHashMapCreate(
         const CxAllocator *allocator,
         size_t itemsize,
@@ -126,6 +127,7 @@
  * @retval non-zero if a memory allocation error occurred
  */
 cx_attr_nonnull
+cx_attr_export
 int cxMapRehash(CxMap *map);
 
 
--- a/src/cx/iterator.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/iterator.h	Mon Feb 10 20:59:02 2025 +0100
@@ -49,7 +49,6 @@
     /**
      * True iff the iterator points to valid data.
      */
-    cx_attr_nonnull
     bool (*valid)(const void *);
 
     /**
@@ -57,15 +56,11 @@
      *
      * When valid returns false, the behavior of this function is undefined.
      */
-    cx_attr_nonnull
-    cx_attr_nodiscard
     void *(*current)(const void *);
 
     /**
      * Original implementation in case the function needs to be wrapped.
      */
-    cx_attr_nonnull
-    cx_attr_nodiscard
     void *(*current_impl)(const void *);
 
     /**
@@ -73,7 +68,6 @@
      *
      * When valid returns false, the behavior of this function is undefined.
      */
-    cx_attr_nonnull
     void (*next)(void *);
     /**
      * Indicates whether this iterator may remove elements.
@@ -227,6 +221,7 @@
  * @see cxIteratorPtr()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxIterator(
         const void *array,
         size_t elem_size,
@@ -257,6 +252,7 @@
  * @return an iterator for the specified array
  */
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxMutIterator(
         void *array,
         size_t elem_size,
@@ -278,6 +274,7 @@
  * @see cxIterator()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxIteratorPtr(
         const void *array,
         size_t elem_count
@@ -298,6 +295,7 @@
  * @see cxIteratorPtr()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxMutIteratorPtr(
         void *array,
         size_t elem_count,
--- a/src/cx/json.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/json.h	Mon Feb 10 20:59:02 2025 +0100
@@ -475,6 +475,7 @@
  * @return new JSON writer settings
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonWriter cxJsonWriterCompact(void);
 
 /**
@@ -484,6 +485,7 @@
  * @return new JSON writer settings
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonWriter cxJsonWriterPretty(bool use_spaces);
 
 /**
@@ -507,6 +509,7 @@
  * @retval non-zero when no or not all data could be written
  */
 cx_attr_nonnull_arg(1, 2, 3)
+cx_attr_export
 int cxJsonWrite(
     void* target,
     const CxJsonValue* value,
@@ -522,6 +525,7 @@
  * @see cxJsonDestroy()
  */
 cx_attr_nonnull_arg(1)
+cx_attr_export
 void cxJsonInit(CxJson *json, const CxAllocator *allocator);
 
 /**
@@ -531,6 +535,7 @@
  * @see cxJsonInit()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxJsonDestroy(CxJson *json);
 
 /**
@@ -568,6 +573,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonFilln(CxJson *json, const char *buf, size_t len);
 
 #ifdef __cplusplus
@@ -668,6 +674,7 @@
  * @see cxJsonArrAddValues()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator);
 
 /**
@@ -679,6 +686,7 @@
  * @see cxJsonArrAddValues()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator);
 
 /**
@@ -691,6 +699,7 @@
  * @see cxJsonArrAddNumbers()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num);
 
 /**
@@ -703,6 +712,7 @@
  * @see cxJsonArrAddIntegers()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num);
 
 /**
@@ -718,6 +728,7 @@
 cx_attr_nodiscard
 cx_attr_nonnull_arg(2)
 cx_attr_cstr_arg(2)
+cx_attr_export
 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str);
 
 /**
@@ -731,6 +742,7 @@
  * @see cxJsonArrAddCxStrings()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str);
 
 /**
@@ -743,6 +755,7 @@
  * @see cxJsonArrAddLiterals()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit);
 
 /**
@@ -756,6 +769,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count);
 
 /**
@@ -769,6 +783,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count);
 
 /**
@@ -785,6 +800,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count);
 
 /**
@@ -801,6 +817,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count);
 
 /**
@@ -814,6 +831,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count);
 
 /**
@@ -830,6 +848,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count);
 
 /**
@@ -847,6 +866,7 @@
  * @retval non-zero allocation failure
  */
 cx_attr_nonnull
+cx_attr_export
 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child);
 
 /**
@@ -859,6 +879,7 @@
  * @see cxJsonCreateObj()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name);
 
 /**
@@ -871,6 +892,7 @@
  * @see cxJsonCreateArr()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name);
 
 /**
@@ -884,6 +906,7 @@
  * @see cxJsonCreateNumber()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num);
 
 /**
@@ -897,6 +920,7 @@
  * @see cxJsonCreateInteger()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num);
 
 /**
@@ -913,6 +937,7 @@
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(3)
+cx_attr_export
 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str);
 
 /**
@@ -928,6 +953,7 @@
  * @see cxJsonCreateCxString()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str);
 
 /**
@@ -941,6 +967,7 @@
  * @see cxJsonCreateLiteral()
  */
 cx_attr_nonnull
+cx_attr_export
 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit);
 
 /**
@@ -954,6 +981,7 @@
  *
  * @param value the value
  */
+cx_attr_export
 void cxJsonValueFree(CxJsonValue *value);
 
 /**
@@ -980,6 +1008,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_w(2)
+cx_attr_export
 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value);
 
 /**
@@ -1252,6 +1281,7 @@
  */
 cx_attr_nonnull
 cx_attr_returns_nonnull
+cx_attr_export
 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index);
 
 /**
@@ -1267,6 +1297,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxJsonArrIter(const CxJsonValue *value);
 
 /**
@@ -1283,6 +1314,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxJsonObjIter(const CxJsonValue *value);
 
 /**
@@ -1290,20 +1322,21 @@
  */
 cx_attr_nonnull
 cx_attr_returns_nonnull
+cx_attr_export
 CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name);
 
 #ifdef __cplusplus
 } // extern "C"
 
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) {
     return cx_json_obj_get_cxstr(value, name);
 }
 
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) {
     return cx_json_obj_get_cxstr(value, cx_strcast(name));
 }
 
-CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
+static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) {
     return cx_json_obj_get_cxstr(value, cx_str(name));
 }
 
--- a/src/cx/linked_list.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/linked_list.h	Mon Feb 10 20:59:02 2025 +0100
@@ -61,6 +61,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxListFree, 1)
+cx_attr_export
 CxList *cxLinkedListCreate(
         const CxAllocator *allocator,
         cx_compare_func comparator,
@@ -103,6 +104,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 void *cx_linked_list_at(
         const void *start,
         size_t start_index,
@@ -123,6 +125,7 @@
  * @return the index of the element, if found - unspecified if not found
  */
 cx_attr_nonnull_arg(1, 4, 5)
+cx_attr_export
 void *cx_linked_list_find(
         const void *start,
         ptrdiff_t loc_advance,
@@ -145,6 +148,7 @@
  */
 cx_attr_nonnull
 cx_attr_returns_nonnull
+cx_attr_export
 void *cx_linked_list_first(
         const void *node,
         ptrdiff_t loc_prev
@@ -163,6 +167,7 @@
  */
 cx_attr_nonnull
 cx_attr_returns_nonnull
+cx_attr_export
 void *cx_linked_list_last(
         const void *node,
         ptrdiff_t loc_next
@@ -179,6 +184,7 @@
  * @return the node or @c NULL if @p node has no predecessor
  */
 cx_attr_nonnull
+cx_attr_export
 void *cx_linked_list_prev(
         const void *begin,
         ptrdiff_t loc_next,
@@ -198,6 +204,7 @@
  * @param new_node a pointer to the node that shall be appended
  */
 cx_attr_nonnull_arg(5)
+cx_attr_export
 void cx_linked_list_add(
         void **begin,
         void **end,
@@ -219,6 +226,7 @@
  * @param new_node a pointer to the node that shall be prepended
  */
 cx_attr_nonnull_arg(5)
+cx_attr_export
 void cx_linked_list_prepend(
         void **begin,
         void **end,
@@ -236,6 +244,7 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_linked_list_link(
         void *left,
         void *right,
@@ -254,6 +263,7 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_linked_list_unlink(
         void *left,
         void *right,
@@ -276,6 +286,7 @@
  * @param new_node a pointer to the node that shall be inserted
  */
 cx_attr_nonnull_arg(6)
+cx_attr_export
 void cx_linked_list_insert(
         void **begin,
         void **end,
@@ -306,6 +317,7 @@
  * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
  */
 cx_attr_nonnull_arg(6)
+cx_attr_export
 void cx_linked_list_insert_chain(
         void **begin,
         void **end,
@@ -331,6 +343,7 @@
  * @param cmp_func a compare function that will receive the node pointers
  */
 cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_export
 void cx_linked_list_insert_sorted(
         void **begin,
         void **end,
@@ -360,6 +373,7 @@
  * @param cmp_func a compare function that will receive the node pointers
  */
 cx_attr_nonnull_arg(1, 5, 6)
+cx_attr_export
 void cx_linked_list_insert_sorted_chain(
         void **begin,
         void **end,
@@ -391,6 +405,7 @@
  * @return the actual number of nodes that were removed (can be less when the list did not have enough nodes)
  */
 cx_attr_nonnull_arg(5)
+cx_attr_export
 size_t cx_linked_list_remove_chain(
         void **begin,
         void **end,
@@ -437,6 +452,8 @@
  * @param loc_next the location of the @c next pointer within the node struct
  * @return the size of the list or zero if @p node is @c NULL
  */
+cx_attr_nodiscard
+cx_attr_export
 size_t cx_linked_list_size(
         const void *node,
         ptrdiff_t loc_next
@@ -465,6 +482,7 @@
  * @param cmp_func the compare function defining the sort order
  */
 cx_attr_nonnull_arg(1, 6)
+cx_attr_export
 void cx_linked_list_sort(
         void **begin,
         void **end,
@@ -489,6 +507,7 @@
  * right list, positive if the left list is larger than the right list, zero if both lists are equal.
  */
 cx_attr_nonnull_arg(5)
+cx_attr_export
 int cx_linked_list_compare(
         const void *begin_left,
         const void *begin_right,
@@ -506,6 +525,7 @@
  * @param loc_next the location of a @c next pointer within your node struct (required)
  */
 cx_attr_nonnull_arg(1)
+cx_attr_export
 void cx_linked_list_reverse(
         void **begin,
         void **end,
--- a/src/cx/list.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/list.h	Mon Feb 10 20:59:02 2025 +0100
@@ -219,6 +219,7 @@
  * @return the number of elements actually inserted
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cx_list_default_insert_array(
         struct cx_list_s *list,
         size_t index,
@@ -243,6 +244,7 @@
  * @return the number of elements actually inserted
  */
 cx_attr_nonnull
+cx_attr_export
 size_t cx_list_default_insert_sorted(
         struct cx_list_s *list,
         const void *sorted_data,
@@ -261,6 +263,7 @@
  * @param list the list that shall be sorted
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_list_default_sort(struct cx_list_s *list);
 
 /**
@@ -277,6 +280,7 @@
  * allocation for the temporary buffer fails
  */
 cx_attr_nonnull
+cx_attr_export
 int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j);
 
 /**
@@ -324,6 +328,7 @@
  * @param elem_size the size of one element
  */
 cx_attr_nonnull_arg(1, 2, 3)
+cx_attr_export
 void cx_list_init(
     struct cx_list_s *list,
     struct cx_list_class_s *cl,
@@ -739,6 +744,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxListMutIteratorAt(
         CxList *list,
         size_t index
@@ -758,6 +764,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxIterator cxListMutBackwardsIteratorAt(
         CxList *list,
         size_t index
@@ -921,6 +928,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 int cxListCompare(
         const CxList *list,
         const CxList *other
@@ -933,6 +941,7 @@
  *
  * @param list the list which shall be freed
  */
+cx_attr_export
 void cxListFree(CxList *list);
 
 /**
@@ -943,6 +952,7 @@
  * You can use this is a placeholder for initializing CxList pointers
  * for which you do not want to reserve memory right from the beginning.
  */
+cx_attr_export
 extern CxList *const cxEmptyList;
 
 
--- a/src/cx/map.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/map.h	Mon Feb 10 20:59:02 2025 +0100
@@ -230,6 +230,7 @@
  * You can use this is a placeholder for initializing CxMap pointers
  * for which you do not want to reserve memory right from the beginning.
  */
+cx_attr_export
 extern CxMap *const cxEmptyMap;
 
 /**
@@ -239,6 +240,7 @@
  *
  * @param map the map to be freed
  */
+cx_attr_export
 void cxMapFree(CxMap *map);
 
 
@@ -338,6 +340,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxMapIterator cxMapMutIteratorValues(CxMap *map);
 
 /**
@@ -354,6 +357,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxMapIterator cxMapMutIteratorKeys(CxMap *map);
 
 /**
@@ -372,6 +376,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxMapIterator cxMapMutIterator(CxMap *map);
 
 #ifdef __cplusplus
--- a/src/cx/mempool.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/mempool.h	Mon Feb 10 20:59:02 2025 +0100
@@ -80,6 +80,7 @@
  *
  * @param pool the memory pool to free
  */
+cx_attr_export
 void cxMempoolFree(CxMempool *pool);
 
 /**
@@ -94,6 +95,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxMempoolFree, 1)
+cx_attr_export
 CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr);
 
 /**
@@ -114,6 +116,7 @@
  * @param fnc the destructor function
  */
 cx_attr_nonnull
+cx_attr_export
 void cxMempoolSetDestructor(
         void *memory,
         cx_destructor_func fnc
@@ -128,6 +131,7 @@
  * @param memory the object allocated in the pool
  */
 cx_attr_nonnull
+cx_attr_export
 void cxMempoolRemoveDestructor(void *memory);
 
 /**
@@ -145,6 +149,7 @@
  * @retval non-zero failure
  */
 cx_attr_nonnull
+cx_attr_export
 int cxMempoolRegister(
         CxMempool *pool,
         void *memory,
--- a/src/cx/printf.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/printf.h	Mon Feb 10 20:59:02 2025 +0100
@@ -56,6 +56,7 @@
 /**
  * The maximum string length that fits into stack memory.
  */
+cx_attr_export
 extern const unsigned cx_printf_sbo_size;
 
 /**
@@ -71,6 +72,7 @@
 cx_attr_nonnull_arg(1, 2, 3)
 cx_attr_printf(3, 4)
 cx_attr_cstr_arg(3)
+cx_attr_export
 int cx_fprintf(
         void *stream,
         cx_write_func wfc,
@@ -91,6 +93,7 @@
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(3)
+cx_attr_export
 int cx_vfprintf(
         void *stream,
         cx_write_func wfc,
@@ -115,6 +118,7 @@
 cx_attr_nonnull_arg(1, 2)
 cx_attr_printf(2, 3)
 cx_attr_cstr_arg(2)
+cx_attr_export
 cxmutstr cx_asprintf_a(
         const CxAllocator *allocator,
         const char *fmt,
@@ -153,6 +157,7 @@
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(2)
+cx_attr_export
 cxmutstr cx_vasprintf_a(
         const CxAllocator *allocator,
         const char *fmt,
@@ -222,6 +227,7 @@
 cx_attr_nonnull_arg(1, 2, 3, 4)
 cx_attr_printf(4, 5)
 cx_attr_cstr_arg(4)
+cx_attr_export
 int cx_sprintf_a(
         CxAllocator *alloc,
         char **str,
@@ -266,6 +272,7 @@
 cx_attr_cstr_arg(4)
 cx_attr_access_rw(2)
 cx_attr_access_rw(3)
+cx_attr_export
 int cx_vsprintf_a(
         CxAllocator *alloc,
         char **str,
@@ -324,6 +331,7 @@
 cx_attr_access_rw(2)
 cx_attr_access_rw(3)
 cx_attr_access_rw(4)
+cx_attr_export
 int cx_sprintf_sa(
         CxAllocator *alloc,
         char *buf,
@@ -378,6 +386,7 @@
  */
 cx_attr_nonnull
 cx_attr_cstr_arg(5)
+cx_attr_export
 int cx_vsprintf_sa(
         CxAllocator *alloc,
         char *buf,
--- a/src/cx/properties.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/properties.h	Mon Feb 10 20:59:02 2025 +0100
@@ -91,6 +91,7 @@
 /**
  * Default properties configuration.
  */
+cx_attr_export
 extern const CxPropertiesConfig cx_properties_config_default;
 
 /**
@@ -327,6 +328,7 @@
  * @see cxPropertiesInitDefault()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config);
 
 /**
@@ -341,6 +343,7 @@
  * @param prop the properties interface
  */
 cx_attr_nonnull
+cx_attr_export
 void cxPropertiesDestroy(CxProperties *prop);
 
 /**
@@ -390,6 +393,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 int cxPropertiesFilln(
         CxProperties *prop,
         const char *buf,
@@ -495,6 +499,7 @@
  * @param capacity the capacity of the stack memory
  */
 cx_attr_nonnull
+cx_attr_export
 void cxPropertiesUseStack(
         CxProperties *prop,
         char *buf,
@@ -533,6 +538,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxPropertiesStatus cxPropertiesNext(
         CxProperties *prop,
         cxstring *key,
@@ -553,6 +559,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 CxPropertiesSink cxPropertiesMapSink(CxMap *map);
 
 /**
@@ -563,6 +570,7 @@
  * @see cxPropertiesLoad()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxPropertiesSource cxPropertiesStringSource(cxstring str);
 
 /**
@@ -576,6 +584,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_access_r(1, 2)
+cx_attr_export
 CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len);
 
 /**
@@ -591,6 +600,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
+cx_attr_export
 CxPropertiesSource cxPropertiesCstrSource(const char *str);
 
 /**
@@ -605,6 +615,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_access_r(1)
+cx_attr_export
 CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size);
 
 
@@ -637,6 +648,7 @@
  * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed
  */
 cx_attr_nonnull
+cx_attr_export
 CxPropertiesStatus cxPropertiesLoad(
         CxProperties *prop,
         CxPropertiesSink sink,
--- a/src/cx/streams.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/streams.h	Mon Feb 10 20:59:02 2025 +0100
@@ -65,6 +65,7 @@
 cx_attr_access_r(1)
 cx_attr_access_w(2)
 cx_attr_access_w(5)
+cx_attr_export
 size_t cx_stream_bncopy(
         void *src,
         void *dest,
@@ -106,6 +107,7 @@
 cx_attr_nonnull
 cx_attr_access_r(1)
 cx_attr_access_w(2)
+cx_attr_export
 size_t cx_stream_ncopy(
         void *src,
         void *dest,
--- a/src/cx/string.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/string.h	Mon Feb 10 20:59:02 2025 +0100
@@ -42,6 +42,7 @@
 /**
  * The maximum length of the "needle" in cx_strstr() that can use SBO.
  */
+cx_attr_export
 extern const unsigned cx_strstr_sbo_size;
 
 /**
@@ -175,6 +176,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
+cx_attr_export
 cxmutstr cx_mutstr(char *cstring);
 
 /**
@@ -195,6 +197,7 @@
  */
 cx_attr_nodiscard
 cx_attr_access_rw(1, 2)
+cx_attr_export
 cxmutstr cx_mutstrn(
         char *cstring,
         size_t length
@@ -218,6 +221,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_cstr_arg(1)
+cx_attr_export
 cxstring cx_str(const char *cstring);
 
 
@@ -239,6 +243,7 @@
  */
 cx_attr_nodiscard
 cx_attr_access_r(1, 2)
+cx_attr_export
 cxstring cx_strn(
         const char *cstring,
         size_t length
@@ -307,6 +312,7 @@
  *
  * @param str the string to free
  */
+cx_attr_export
 void cx_strfree(cxmutstr *str);
 
 /**
@@ -323,6 +329,7 @@
  * @param str the string to free
  */
 cx_attr_nonnull_arg(1)
+cx_attr_export
 void cx_strfree_a(
         const CxAllocator *alloc,
         cxmutstr *str
@@ -341,6 +348,7 @@
  * @return the accumulated length of all strings
  */
 cx_attr_nodiscard
+cx_attr_export
 size_t cx_strlen(
         size_t count,
         ...
@@ -370,6 +378,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 cxmutstr cx_strcat_ma(
         const CxAllocator *alloc,
         cxmutstr str,
@@ -458,6 +467,7 @@
  * @see cx_strsubsl_m()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strsubs(
         cxstring string,
         size_t start
@@ -483,6 +493,7 @@
  * @see cx_strsubsl_m()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strsubsl(
         cxstring string,
         size_t start,
@@ -505,6 +516,7 @@
  * @see cx_strsubsl()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strsubs_m(
         cxmutstr string,
         size_t start
@@ -530,6 +542,7 @@
  * @see cx_strsubsl()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strsubsl_m(
         cxmutstr string,
         size_t start,
@@ -549,6 +562,7 @@
  * @see cx_strchr_m()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strchr(
         cxstring string,
         int chr
@@ -567,6 +581,7 @@
  * @see cx_strchr()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strchr_m(
         cxmutstr string,
         int chr
@@ -585,6 +600,7 @@
  * @see cx_strrchr_m()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strrchr(
         cxstring string,
         int chr
@@ -603,6 +619,7 @@
  * @see cx_strrchr()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strrchr_m(
         cxmutstr string,
         int chr
@@ -625,6 +642,7 @@
  * @see cx_strstr_m()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strstr(
         cxstring haystack,
         cxstring needle
@@ -647,6 +665,7 @@
  * @see cx_strstr()
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strstr_m(
         cxmutstr haystack,
         cxstring needle
@@ -667,6 +686,7 @@
 cx_attr_nodiscard
 cx_attr_nonnull
 cx_attr_access_w(4, 3)
+cx_attr_export
 size_t cx_strsplit(
         cxstring string,
         cxstring delim,
@@ -696,6 +716,7 @@
 cx_attr_nodiscard
 cx_attr_nonnull
 cx_attr_access_w(5)
+cx_attr_export
 size_t cx_strsplit_a(
         const CxAllocator *allocator,
         cxstring string,
@@ -720,6 +741,7 @@
 cx_attr_nodiscard
 cx_attr_nonnull
 cx_attr_access_w(4, 3)
+cx_attr_export
 size_t cx_strsplit_m(
         cxmutstr string,
         cxstring delim,
@@ -749,6 +771,7 @@
 cx_attr_nodiscard
 cx_attr_nonnull
 cx_attr_access_w(5)
+cx_attr_export
 size_t cx_strsplit_ma(
         const CxAllocator *allocator,
         cxmutstr string,
@@ -766,6 +789,7 @@
  * than @p s2, zero if both strings equal
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_strcmp(
         cxstring s1,
         cxstring s2
@@ -780,6 +804,7 @@
  * than @p s2, zero if both strings equal ignoring case
  */
 cx_attr_nodiscard
+cx_attr_export
 int cx_strcasecmp(
         cxstring s1,
         cxstring s2
@@ -797,6 +822,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 int cx_strcmp_p(
         const void *s1,
         const void *s2
@@ -814,6 +840,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 int cx_strcasecmp_p(
         const void *s1,
         const void *s2
@@ -834,6 +861,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 cxmutstr cx_strdup_a_(
         const CxAllocator *allocator,
         cxstring string
@@ -880,6 +908,7 @@
  * @return the trimmed string
  */
 cx_attr_nodiscard
+cx_attr_export
 cxstring cx_strtrim(cxstring string);
 
 /**
@@ -892,6 +921,7 @@
  * @return the trimmed string
  */
 cx_attr_nodiscard
+cx_attr_export
 cxmutstr cx_strtrim_m(cxmutstr string);
 
 /**
@@ -903,6 +933,7 @@
  * @c false otherwise
  */
 cx_attr_nodiscard
+cx_attr_export
 bool cx_strprefix(
         cxstring string,
         cxstring prefix
@@ -917,6 +948,7 @@
  * @c false otherwise
  */
 cx_attr_nodiscard
+cx_attr_export
 bool cx_strsuffix(
         cxstring string,
         cxstring suffix
@@ -931,6 +963,7 @@
  * @c false otherwise
  */
 cx_attr_nodiscard
+cx_attr_export
 bool cx_strcaseprefix(
         cxstring string,
         cxstring prefix
@@ -945,6 +978,7 @@
  * @c false otherwise
  */
 cx_attr_nodiscard
+cx_attr_export
 bool cx_strcasesuffix(
         cxstring string,
         cxstring suffix
@@ -971,6 +1005,7 @@
  */
 cx_attr_nodiscard
 cx_attr_nonnull
+cx_attr_export
 cxmutstr cx_strreplacen_a(
         const CxAllocator *allocator,
         cxstring str,
@@ -1049,6 +1084,7 @@
  * @return a new string tokenization context
  */
 cx_attr_nodiscard
+cx_attr_export
 CxStrtokCtx cx_strtok_(
         cxstring str,
         cxstring delim,
@@ -1079,6 +1115,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_access_w(2)
+cx_attr_export
 bool cx_strtok_next(
         CxStrtokCtx *ctx,
         cxstring *token
@@ -1101,6 +1138,7 @@
 cx_attr_nonnull
 cx_attr_nodiscard
 cx_attr_access_w(2)
+cx_attr_export
 bool cx_strtok_next_m(
         CxStrtokCtx *ctx,
         cxmutstr *token
@@ -1115,6 +1153,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_r(2, 3)
+cx_attr_export
 void cx_strtok_delim(
         CxStrtokCtx *ctx,
         const cxstring *delim,
@@ -1139,7 +1178,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep);
 
 /**
@@ -1156,7 +1195,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep);
 
 /**
@@ -1173,7 +1212,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep);
 
 /**
@@ -1190,7 +1229,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep);
 
 /**
@@ -1207,7 +1246,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep);
 
 /**
@@ -1224,7 +1263,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep);
 
 /**
@@ -1241,7 +1280,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep);
 
 /**
@@ -1258,7 +1297,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep);
 
 /**
@@ -1275,7 +1314,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep);
 
 /**
@@ -1292,7 +1331,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep);
 
 /**
@@ -1309,7 +1348,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep);
 
 /**
@@ -1326,7 +1365,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep);
 
 /**
@@ -1343,7 +1382,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep);
 
 /**
@@ -1360,7 +1399,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep);
 
 /**
@@ -1377,7 +1416,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep);
 
 /**
@@ -1394,7 +1433,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep);
 
 /**
@@ -1411,7 +1450,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep);
 
 /**
@@ -1428,7 +1467,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep);
 
 /**
@@ -1445,7 +1484,7 @@
  * @retval zero success
  * @retval non-zero conversion was not possible
  */
-cx_attr_access_w(2) cx_attr_nonnull_arg(2)
+cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export
 int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep);
 
 /**
--- a/src/cx/tree.h	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/cx/tree.h	Mon Feb 10 20:59:02 2025 +0100
@@ -263,6 +263,7 @@
  * @see cx_tree_unlink()
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_tree_link(
         void *parent,
         void *node,
@@ -288,6 +289,7 @@
  * @see cx_tree_link()
  */
 cx_attr_nonnull
+cx_attr_export
 void cx_tree_unlink(
         void *node,
         ptrdiff_t loc_parent,
@@ -387,6 +389,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_w(5)
+cx_attr_export
 int cx_tree_search_data(
         const void *root,
         size_t depth,
@@ -423,6 +426,7 @@
  */
 cx_attr_nonnull
 cx_attr_access_w(5)
+cx_attr_export
 int cx_tree_search(
         const void *root,
         size_t depth,
@@ -454,6 +458,7 @@
  * @see cxTreeIteratorDispose()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxTreeIterator cx_tree_iterator(
         void *root,
         bool visit_on_exit,
@@ -480,6 +485,7 @@
  * @see cxTreeVisitorDispose()
  */
 cx_attr_nodiscard
+cx_attr_export
 CxTreeVisitor cx_tree_visitor(
         void *root,
         ptrdiff_t loc_children,
@@ -505,6 +511,7 @@
  * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to
  * implement optimized insertion of multiple elements into a tree.
  */
+cx_attr_export
 extern unsigned int cx_tree_add_look_around_depth;
 
 /**
@@ -547,6 +554,7 @@
  */
 cx_attr_nonnull_arg(1, 3, 4, 6, 7)
 cx_attr_access_w(6)
+cx_attr_export
 size_t cx_tree_add_iter(
         struct cx_iterator_base_s *iter,
         size_t num,
@@ -601,6 +609,7 @@
  */
 cx_attr_nonnull_arg(1, 4, 5, 7, 8)
 cx_attr_access_w(7)
+cx_attr_export
 size_t cx_tree_add_array(
         const void *src,
         size_t num,
@@ -664,6 +673,7 @@
  */
 cx_attr_nonnull_arg(1, 2, 3, 5, 6)
 cx_attr_access_w(5)
+cx_attr_export
 int cx_tree_add(
         const void *src,
         cx_tree_search_func sfunc,
@@ -894,6 +904,7 @@
  * @see cxTreeFree()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxTreeDestroySubtree(CxTree *tree, void *node);
 
 
@@ -932,6 +943,7 @@
  *
  * @param tree the tree to free
  */
+cx_attr_export
 void cxTreeFree(CxTree *tree);
 
 /**
@@ -962,6 +974,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxTreeFree, 1)
+cx_attr_export
 CxTree *cxTreeCreate(
         const CxAllocator *allocator,
         cx_tree_node_create_func create_func,
@@ -1022,6 +1035,7 @@
 cx_attr_nodiscard
 cx_attr_malloc
 cx_attr_dealloc(cxTreeFree, 1)
+cx_attr_export
 CxTree *cxTreeCreateWrapped(
         const CxAllocator *allocator,
         void *root,
@@ -1158,6 +1172,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root);
 
 /**
@@ -1169,6 +1184,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root);
 
 /**
@@ -1179,6 +1195,7 @@
  */
 cx_attr_nonnull
 cx_attr_nodiscard
+cx_attr_export
 size_t cxTreeDepth(CxTree *tree);
 
 /**
@@ -1267,6 +1284,7 @@
  * @see cxTreeAddChildNode()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxTreeSetParent(
         CxTree *tree,
         void *parent,
@@ -1289,6 +1307,7 @@
  * @see cxTreeSetParent()
  */
 cx_attr_nonnull
+cx_attr_export
 void cxTreeAddChildNode(
         CxTree *tree,
         void *parent,
@@ -1313,6 +1332,7 @@
  * @see cxTreeInsert()
  */
 cx_attr_nonnull
+cx_attr_export
 int cxTreeAddChild(
         CxTree *tree,
         void *parent,
@@ -1353,6 +1373,7 @@
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
 cx_attr_nonnull_arg(1, 2)
+cx_attr_export
 int cxTreeRemoveNode(
         CxTree *tree,
         void *node,
@@ -1371,6 +1392,7 @@
  * @param node the node to remove
  */
 cx_attr_nonnull
+cx_attr_export
 void cxTreeRemoveSubtree(CxTree *tree, void *node);
 
 /**
@@ -1392,6 +1414,7 @@
  * @return zero on success, non-zero if @p node is the root node of the tree
  */
 cx_attr_nonnull_arg(1, 2)
+cx_attr_export
 int cxTreeDestroyNode(
         CxTree *tree,
         void *node,
--- a/src/properties.c	Mon Feb 10 21:30:51 2025 +0100
+++ b/src/properties.c	Mon Feb 10 20:59:02 2025 +0100
@@ -252,7 +252,7 @@
 CxPropertiesSink cxPropertiesMapSink(CxMap *map) {
     CxPropertiesSink sink;
     sink.sink = map;
-    sink.data = cxDefaultAllocator;
+    sink.data = (void*) cxDefaultAllocator;
     sink.sink_func = cx_properties_sink_map;
     return sink;
 }
--- a/tests/test_tree.c	Mon Feb 10 21:30:51 2025 +0100
+++ b/tests/test_tree.c	Mon Feb 10 20:59:02 2025 +0100
@@ -56,7 +56,7 @@
 
 static void *tree_node_file_create(
         const void *dptr,
-        void *allocator) {
+        const void *allocator) {
     if (allocator == NULL) allocator = cxDefaultAllocator;
 
     tree_node_file *node = cxMalloc(allocator, sizeof(tree_node_file));

mercurial