Почему я не могу использовать функции из DLL, которые содержат auto в качестве параметра или возвращаемого типа? - PullRequest
0 голосов
/ 24 апреля 2019

Я экспериментировал с динамическим выделением памяти для создаваемого языка программирования. Мой основной проект сделан на C #, но у меня есть C ++ DLL, содержащая методы для создания переменных. Методы DLL загружаются с атрибутом System.Runtime.InteropServices.DllImport() в C #. Я обнаружил, что при сборке моего файла C ++ в DLL (с ​​использованием g ++ v6.3.0) любые функции, которые возвращали auto или имели параметры auto, не экспортировались в DLL. Я проверил с dumpbin -exports и обнаружил, что они не были включены. Мне удалось исправить DLL, но мне стало интересно, почему тип auto не экспортируется.

Я знаю, что это не просто проблема C # (я тестировал ее на других языках, таких как Python, и это было до того, как я выяснил, что проблема была проблемой компиляции), и это не просто проблема g ++, потому что другие компиляторы также не могут экспортировать функции. Как ни странно, нигде в процессе не возникает никаких ошибок.

Оригинальный код:

// Variable Manipulation methods
extern "C" {
    auto CreateVariable(auto value) {
        // Create a variable and return its address
        // Here, C++ deduces that "value" is any type and the function creates a new object of that type, pointing to the value
        return new (typeof(value))(value);
    }

    auto GetVariable(auto address) {
        // Return the value of a variable from the specified address
        // Here, C++ deduces that "address" is a pointer of some sort
        return *addr;
    }

    void SetVariable(auto address, auto value) {
        // Set the variable using its specified address
        // Here, C++ deduces that "address" is a pointer and "value" is any type
        *addr = value;
    }

    void DeleteVariable(auto address) {
        // Delete the variable.
        // Here, C++ deduces that "address" is a pointer of some sort
        delete addr;
    }
}

Я ожидал, что смогу использовать

[DllImport("dll_path.dll")]
public static extern IntPtr CreateVariable([MarshalAs(UnmanagedType.Any)] object value);

[DllImport("dll_path.dll")]
public static extern object GetVariable(IntPtr address);

[DllImport("dll_path.dll")]
public static extern void SetVariable(IntPtr address, [MarshalAs(UnmanagedType.Any] object value);

[DllImport("dll_path.dll")]
public static extern void DeleteVariable(IntPtr address);

в моей программе на C #, но он продолжал выдавать System.EntryPointNotFoundException, говоря, что точка входа не может быть найдена. Естественно, я подозревал, что C # просто требователен к DLL, но я тестировал на других языках, таких как модуль ctypes Python, и он выдал ту же ошибку. Я нашел решение: используя тип windows.h BYTE (unsigned char).

У меня такой вопрос: почему я не могу экспортировать функции с auto параметрами или auto типами возвращаемых данных?

1 Ответ

2 голосов
/ 24 апреля 2019

Прежде всего, auto не является динамическим типом в C ++. Ключевое слово auto является своего рода заполнителем для вывода компилятором:

// int i1 = 0; // same thing in the eyes of the compiler
   auto i1 = 0;

Но компилятор выводит типы только тогда, когда он инициализируется значением.

// ???? i2;
// i2 = 0;

   auto i2; // error! deduce what type?
   i2 = 0;  // cannot call operator= with unknown type

Но вы можете иметь auto в лямбда-типе, что там отличается?

// type of `lambda` is an unnamed type.
auto lambda = [](auto var) { std::cout << var; };

lambda(1);
lambda("string");

Даже если это выглядит динамично, не так ли? Общая лямбда реализована с использованием шаблона:

//    v----- unnamed
struct {
    template<typename T>
    auto operator()(auto var) const {
        std::cout << var;
    }
} lambda{};

Это означает, что компилятор сгенерирует новый статический код на лету для параметра auto . Что значит, даже если вы обновились до C ++ 20, что позволяет:

auto CreateVariable(auto value) {
    // ...
}

На самом деле никакой функции не существует. Это просто шаблон, ожидающий реализации.

Не будет функции для экспорта из вашей dll, так как это будут только некоторые шаблоны.

То, что вы ищете, выглядит примерно так:

struct CSharpObject;

auto CreateVariable(CSharpObject* value) -> CSharpObject* {
    // reflect on value to check the type
    // construct a new instance of CSharpObject with the
    // right set of metadata for c# to understand
}

К сожалению, C ++ не понимает динамический, отражаемый и собираемый мусор объект C #, а C # не понимает статическую природу типов шаблонов и значений C ++.

Вам потребуется предоставить набор функций, которые работают с набором известных типов:

auto CreateVariableInt(int value) -> int* {
    return new int{value};
}

auto GetVariableInt(int* address) -> int {
    return *addr;
}

auto CreateVariableDouble(double value) -> double* {
    return new double{value};
}

auto CreateVariableDouble(double* address) -> double {
    return *address;
}

// so on and so forth for every supported types.

Затем на стороне C # сохраните метаданные о том, какой тип содержится, и вызовите нужную функцию.

...