mono / CppSharp

Tools and libraries to glue C/C++ APIs to high-level languages
MIT License
3.04k stars 499 forks source link

Generating bindings for qpdf generates no functions #1660

Open RedMindZ opened 2 years ago

RedMindZ commented 2 years ago

Brief Description

I am trying to generate C# bindings for the library qpdf but the resulting file contains non of the exported functions and classes.

OS: Windows 10 (version 10.0.19044)

Used headers

qpdf offers both a C header (with C functions) and a C++ header (with C++ classes), and both result in essentially empty files. The headers look like this:

DLL.h (shared header with dll export definitions):

#ifndef QPDF_DLL_HH
#define QPDF_DLL_HH

/* The first version of qpdf to include the version constants is 10.6.0. */
#define QPDF_MAJOR_VERSION 10
#define QPDF_MINOR_VERSION 6
#define QPDF_PATCH_VERSION 3
#define QPDF_VERSION "10.6.3"

#if (defined _WIN32 || defined __CYGWIN__) && defined(DLL_EXPORT)
# define QPDF_DLL __declspec(dllexport)
#elif defined __GNUC__
# define QPDF_DLL __attribute__ ((visibility ("default")))
#else
# define QPDF_DLL
#endif
#ifdef __GNUC__
# define QPDF_DLL_CLASS QPDF_DLL
#else
# define QPDF_DLL_CLASS
#endif

#endif /* QPDF_DLL_HH */

qpdf-c.h (the C header, summary):

#ifndef QPDF_C_H
#define QPDF_C_H

#include <qpdf/Constants.h>
#include <qpdf/DLL.h>
#include <qpdf/Types.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

    typedef struct _qpdf_data* qpdf_data;
    typedef struct _qpdf_error* qpdf_error;

    typedef int QPDF_ERROR_CODE;
#define QPDF_SUCCESS 0
#define QPDF_WARNINGS 1 << 0
#define QPDF_ERRORS 1 << 1

    typedef int QPDF_BOOL;
#define QPDF_TRUE 1
#define QPDF_FALSE 0

    QPDF_DLL
    void qpdf_silence_errors(qpdf_data qpdf);

    QPDF_DLL
    char const* qpdf_get_qpdf_version();

    .
    .
    .

    QPDF_DLL
    QPDF_ERROR_CODE qpdf_remove_page(qpdf_data qpdf, qpdf_oh page);
#ifdef __cplusplus
}
#endif

#endif /* QPDF_C_H */

QPDF.hh (the C++ header, summary):

#ifndef QPDF_HH
#define QPDF_HH

#include <qpdf/DLL.h>
#include <qpdf/Types.h>

#include <functional>
.
.
.
#include <vector>

#include <qpdf/Buffer.hh>
.
.
.
#include <qpdf/QPDFXRefEntry.hh>

class QPDF_Stream;
class BitStream;
class BitWriter;

class QPDF
{
  public:
    QPDF_DLL
    static std::string const& QPDFVersion();

    QPDF_DLL
    QPDF();
    QPDF_DLL
    ~QPDF();

    QPDF_DLL
    void processFile(char const* filename, char const* password = 0);

    .
    .
    .
};

#endif // QPDF_HH

Used settings

My driver implementation is as follows:

ConsoleDriver.Run(new QpdfSharpLibrary());

public class QpdfSharpLibrary : ILibrary
{
    public void Setup(Driver driver)
    {
        var options = driver.Options;
        options.GeneratorKind = GeneratorKind.CSharp;
        options.OutputDir = "CppSharpOutput";

        var parserOptions = driver.ParserOptions;
        parserOptions.AddIncludeDirs(@"D:\Programing Stuff\Libraries\Qpdf\qpdf-cpp-10.6.3\include");
        parserOptions.Verbose = true;

        var module = options.AddModule("qpdf");
        module.Headers.Add(@"qpdf\qpdf-c.h");
        //module.Headers.Add(@"qpdf\QPDF.hh");
        module.LibraryDirs.Add(@"D:\Programing Stuff\Libraries\Qpdf\qpdf-cpp-10.6.3\lib");
        module.Libraries.Add(@"qpdf.lib");
    }

    public void SetupPasses(Driver driver)
    {
    }

    public void Preprocess(Driver driver, ASTContext ctx)
    {
    }

    public void Postprocess(Driver driver, ASTContext ctx)
    {
    }
}

Generated code

When I run my driver with the C header (qpdf-c.h) I get a nearly empty file (this is the entire file):

// ----------------------------------------------------------------------------
// <auto-generated>
// This is autogenerated code by CppSharp.
// Do not edit this file or all your changes will be lost after re-generation.
// </auto-generated>
// ----------------------------------------------------------------------------
using System;
using System.Runtime.InteropServices;
using System.Security;
using __CallingConvention = global::System.Runtime.InteropServices.CallingConvention;
using __IntPtr = global::System.IntPtr;

When I run my driver with the C++ header (QPDF.hh) only some classes from the standard library get generated, but nothing from qpdf (this is not the full file. it also contains some other classes and empty Std namespaces that I omitted):

namespace Std
{
    namespace StringVal
    {
        [StructLayout(LayoutKind.Sequential, Size = 32, Pack = 8)]
        public unsafe partial struct __Internal
        {
            internal global::Std.StringVal.Bxty.__Internal _Bx;
            internal ulong _Mysize;
            internal ulong _Myres;
        }
    }
    .
    .
    .
    namespace Allocator
    {
        [StructLayout(LayoutKind.Sequential, Size = 1, Pack = 8)]
        public unsafe partial struct __Internal
        {
            [SuppressUnmanagedCodeSecurity, DllImport("Std-symbols", EntryPoint = "??0?$allocator@D@std@@QEAA@XZ", CallingConvention = __CallingConvention.Cdecl)]
            internal static extern __IntPtr ctorc__N_std_S_allocator__C(__IntPtr __instance);
        }
    }
}

To summarize, I don't know why my resulting files are empty. I assume I might need to add some passes to the driver, but I am not sure which, if any. If anyone could help me with this, it would be greatly appreciated.

tritao commented 2 years ago

Hey @RedMindZ, can you run with options.Verbose = true and post the output?

RedMindZ commented 2 years ago

There you go:

Parsing libraries...
Parsed 'qpdf.lib'
Parsing code...
Compiler argument: -xc++
Compiler argument: -std=gnu++14
Compiler argument: -fno-rtti
Compiler argument: -fms-extensions
Compiler argument: -fms-compatibility
Compiler argument: -fdelayed-template-parsing
Target triple: x86_64-pc-windows-msvc
#include "..." search starts here:
#include <...> search starts here:
 D:\Programing Stuff\Libraries\Qpdf\qpdf-cpp-10.6.3\include
 lib\clang\14.0.0\include
 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include
 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\atlmfc\include
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\winrt
End of search list.
Parsed 'qpdf\QPDF.hh'
Processing code...
Generating code...
Generated 'Std.cs'
tritao commented 2 years ago

Just to make sure, did you activate options.Verbose or parserOptions.Verbose, because they are two different options.

I would expect to see more output with the former, like the passes being run, which I am not seeing.

RedMindZ commented 2 years ago

Sorry, that was parserOptions.Verbose. This is the output: https://gist.github.com/RedMindZ/cda8d487fc0057a171fd4628c3ca15ff (edit: moved to gist)

tritao commented 2 years ago

Well all the QPDF C++ types are getting ignored for some reason, it may be simple but at this point I would need to set it all up and debug to figure out whats happening.

If you dont need the entire API it may be easier to write your own small wrapper / header which you can bind with CppSharp more easily. That's the approach I always recommend since binding complex C++ APIs that depend on C++ standard library is usually complex to do and takes more time.

RedMindZ commented 2 years ago

I actually tested replacing QPDF.hh with a minimal header:

#ifndef QPDF_HH
#define QPDF_HH

class QPDF
{
  public:

    __declspec(dllexport) QPDF();
    __declspec(dllexport) void processFile(char const* filename, char const* password = 0);
    __declspec(dllexport) ~QPDF();
};

#endif // QPDF_HH

When I run the same driver with this header, ~I actually don't see any output~ no file is generated, but the output of the driver looks fine:

Parsing libraries...
Parsed 'qpdf.lib'
Parsing code...
Compiler argument: -xc++
Compiler argument: -std=gnu++14
Compiler argument: -fno-rtti
Compiler argument: -fms-extensions
Compiler argument: -fms-compatibility
Compiler argument: -fdelayed-template-parsing
Target triple: x86_64-pc-windows-msvc
#include "..." search starts here:
#include <...> search starts here:
 D:\Programing Stuff\Libraries\Qpdf\qpdf-cpp-10.6.3\include
 lib\clang\14.0.0\include
 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include
 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\atlmfc\include
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\ucrt
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\shared
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\um
 C:\Program Files (x86)\Windows Kits\10\Include\10.0.19041.0\winrt
End of search list.
Parsed 'qpdf\QPDF-test.hh'
Processing code...
Pass 'CppSharp.Passes.ResolveIncompleteDeclsPass'
Pass 'CppSharp.Passes.IgnoreSystemDeclarationsPass'
Pass 'CppSharp.Passes.MatchParamNamesWithInstantiatedFromPass'
Pass 'CppSharp.Passes.EqualiseAccessOfOverrideAndBasePass'
Pass 'CppSharp.Passes.FlattenAnonymousTypesToFields'
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Pass 'CppSharp.Passes.MarkUsedClassInternalsPass'
Pass 'CppSharp.Passes.TrimSpecializationsPass'
Pass 'CppSharp.Passes.CheckAmbiguousFunctions'
Pass 'CppSharp.Passes.GenerateSymbolsPass'
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Pass 'CppSharp.Passes.MoveFunctionToClassPass'
Pass 'CppSharp.Passes.ValidateOperatorsPass'
Pass 'CppSharp.Passes.FindSymbolsPass'
Pass 'CppSharp.Passes.CheckMacroPass'
Pass 'CppSharp.Passes.CheckStaticClass'
Pass 'CppSharp.Passes.CheckAmbiguousFunctions'
Pass 'CppSharp.Passes.ConstructorToConversionOperatorPass'
Pass 'CppSharp.Passes.MarshalPrimitivePointersAsRefTypePass'
Pass 'CppSharp.Passes.CheckOperatorsOverloadsPass'
Pass 'CppSharp.Passes.CheckVirtualOverrideReturnCovariance'
Pass 'CppSharp.Passes.CleanCommentsPass'
Pass 'CppSharp.Passes.CheckAbiParameters'
Pass 'CppSharp.Passes.CleanInvalidDeclNamesPass'
Pass 'CppSharp.FastDelegateToDelegatesPass'
Pass 'CppSharp.Passes.FieldToPropertyPass'
Pass 'CppSharp.Passes.CheckIgnoredDeclsPass'
Pass 'CppSharp.Passes.CheckFlagEnumsPass'
Pass 'CppSharp.Passes.MakeProtectedNestedTypesPublicPass'
Pass 'CppSharp.Passes.GenerateAbstractImplementationsPass'
Pass 'CppSharp.Passes.MultipleInheritancePass'
Pass 'CppSharp.Passes.DelegatesPass'
Pass 'CppSharp.Passes.GetterSetterToPropertyPass'
Pass 'CppSharp.Passes.StripUnusedSystemTypesPass'
Pass 'CppSharp.Passes.SpecializationMethodsWithDependentPointersPass'
Pass 'CppSharp.Passes.ParamTypeToInterfacePass'
Pass 'CppSharp.Passes.CheckDuplicatedNamesPass'
Pass 'CppSharp.Passes.CaseRenamePass'
Pass 'CppSharp.Passes.CheckKeywordNamesPass'
Pass 'CppSharp.Passes.HandleVariableInitializerPass'
Pass 'CppSharp.MarkEventsWithUniqueIdPass'
Generating code...

I am not sure if I am doing something wrong here, but I believe this should be valid header that would generate at least 3 functions. How should I proceed from here?

neobenedict commented 1 year ago

I actually tested replacing QPDF.hh with a minimal header:

I am not sure if I am doing something wrong here, but I believe this should be valid header that would generate at least 3 functions. How should I proceed from here?

You need to make the class __declspec(dllexport), not the functions.