如何在extern “C”函数中使用std::vector?

huangapple go评论106阅读模式
英文:

How to use std::vector in extern "C" function?

问题

I have a .dll that was written in c++ with extern "C", this function is called add and returns a double. I want to transfer Python's list to c++'s array, and then transfer the array to c++'s vector, so that I can indirectly use the vector data type.

In the add.h:

#pragma once
#include <iostream>
#include <vector>

extern "C" double add(double *arr, int n);
extern "C" double add_raw(std::vector<double> vec);

In the add.cpp:

#include "add.h"

double add(double *arr, int n)
{
    std::vector<double> vec(arr, arr + n);
    return add_raw(vec);
}

double add_raw(std::vector<double> vec)
{
    double sum{};
    // Here is some algorithm developed for vector<double>!!!
    return sum;
}

In add.cpp's code, I referred to the link https://stackoverflow.com/a/49747364/14641812.

In the test.py:

import ctypes

my_dll = ctypes.cdll.LoadLibrary("libAddDll.dll")
my_dll.add.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_int]
my_dll.add.restype = ctypes.c_double
mylist = [1, 2, 3, 4, 5]
myvec = (ctypes.c_double * len(mylist))(*mylist)
result = my_dll.add(myvec, len(mylist))
print(result)

But I get the following error:

Traceback (most recent call last):
  File "f:\testcpp\c++\TestExternalCpp\Test\test1.py", line 11, in <module>
    my_dll = ctypes.cdll.LoadLibrary("./dlls/libAddDll.dll")
  File "D:\Python310\lib\ctypes\__init__.py", line 452, in LoadLibrary
    return self._dlltype(name)
  File "D:\Python310\lib\ctypes\__init__.py", line 374, in __init__
    self._handle = _dlopen(self._name, mode)
FileNotFoundError: Could not find module 'F:\testcpp\c++20\TestExternalCpp\Test\dlls\libAddDll.dll' (or one of its dependencies). Try using the full path with constructor syntax.

Python Version: Python 3.10.10
GCC Version: gcc version 13.1.0 (MinGW-W64)
How to fix it? Thanks.

英文:

I have a .dll that was written in c++ with extern "C", this function is called add and returns a double. I want transfer python's list to c++'s array, and then transfer the array to c++'s vector, so that, I can indirectly use the vector data type.

In the add.h:

#pragma once
#include &lt;iostream&gt;
#include &lt;vector&gt;

extern &quot;C&quot; double add(double *arr, int n);
extern &quot;C&quot; double add_raw(std::vector&lt;double&gt; vec);

In the add.cpp:

#include &quot;add.h&quot;

double add(double *arr, int n)
{
    std::vector&lt;double&gt; vec(arr, arr + n);
    return add_raw(vec);
}

double add_raw(std::vector&lt;double&gt; vec)
{
    double sum{};
    //here some algrothm developed by vector&lt;double&gt;!!!
    return sum;
}

In add.cpp's code, I referred to the link <https://stackoverflow.com/a/49747364/14641812;>

In the test.py:

import ctypes

my_dll = ctypes.cdll.LoadLibrary(&quot;libAddDll.dll&quot;)
my_dll.add.argtypes = [ctypes.POINTER(ctypes.c_double), ctypes.c_int]
my_dll.add.restype = ctypes.c_double
mylist = [1, 2, 3, 4, 5]
myvec = (ctypes.c_double * len(mylist))(*mylist)
result = my_dll.add(myvec, len(mylist))
print(result)

But I get the following error:

Traceback (most recent call last):
  File &quot;f:\testcpp\c++\TestExternalCpp\Test\test1.py&quot;, line 11, in &lt;module&gt;
    my_dll = ctypes.cdll.LoadLibrary(&quot;./dlls/libAddDll.dll&quot;)
  File &quot;D:\Python310\lib\ctypes\__init__.py&quot;, line 452, in LoadLibrary
    return self._dlltype(name)
  File &quot;D:\Python310\lib\ctypes\__init__.py&quot;, line 374, in __init__
    self._handle = _dlopen(self._name, mode)
FileNotFoundError: Could not find module &#39;F:\testcpp\c++20\TestExternalCpp\Test\dlls\libAddDll.dll&#39; (or one of its dependencies). Try using the full path with constructor syntax.

Python Version:Python 3.10.10
GCC Version: gcc version 13.1.0 (MinGW-W64)
How to fix it? Thanks.

答案1

得分: 3

简单的回答是:你不能。

至少在你想要从C代码中使用它时是不可能的。

你不能从C代码中使用C++对象,这是不可能的。你可以使用一个不透明数据类型(通常实现为指向已声明但未定义结构的指针)和一组接受这种数据类型的函数。这些函数被声明为extern "C",如果是在C++中构建的,当然是在C++中实现的,因此它们可以使用该数据类型的对象。

但在这种情况下,也许最好的解决方案是不要混合C可调用和C++可调用的API。在你的头文件中使用#ifdef __cplusplus来区分C API和C++ API:

#ifndef ADD_H
#define ADD_H

#ifdef __cplusplus
// C++ code
#include <vector>

double add_raw(std::vector<double> vec);

// Introduce the functions that can be used either in C or C++
extern "C"
{
#endif

// C or C++ code
double add(double *arr, size_t n);

#ifdef __cplusplus
}  // Close the extern "C" block
#endif

#endif // ADD_H
英文:

> How to use std::vector in extern "C" function?

Simple answer is: You can't.

At least not if you want it to be used from C code.

You can't use C++ objects from C code, that's just not possible. What you can do is use an opaque data type (usually implemented as a pointer to a declared but not defined structure) and a set of functions that accept this data type. The functions are declared as extern &quot;C&quot; if built in C++, and are of course implemented in C++ so they can use the objects of the data type.

But in this case, perhaps the best solution is to not mix the C callable and the C++ callable APIs. Use #ifdef __cplusplus in your header files to separate the C API from the C++ API:

#ifndef ADD_H
#define ADD_H

#ifdef __cplusplus
// C++ code
#include &lt;vector&gt;

double add_raw(std::vector&lt;double&gt; vec);

// Introduce the functions that can be used either in C or C++
extern &quot;C&quot;
{
#endif

// C or C++ code
double add(double *arr, size_t n);

#ifdef __cplusplus
}  // Close the extern &quot;C&quot; block
#endif

#endif // ADD_H

答案2

得分: 2

以下是您要翻译的内容:

There's a fundamental problem:

C doesn't know about std::vector – it even doesn't know about templates and neither about namespaces. So any code containing scope resolution (::) or template instantiations (SomeTemplate<SomeType> or similar) won't compile in C. So

double add_raw(std::vector vec)

is not a valid signature in C, and it is not (while legal) meaningful to provide such functions with C-linkage (i.e. applying extern "C" on them).

Additionally: C doesn't know about extern "C" either.

So you need to hide all of these C++ features away from a C compiler! You can do so by relying on __cplusplus being defined:

#ifdef __cplusplus
// so this is a C++ build!

// only include C++ headers within this section!
#include <vector>;

double add/*_raw*/(std::vector<double> const& data);
//        ^^^^^^^^
// you now even could have an overload, if you wanted to...

extern "C"
#endif
double add(double const* data, size_t length);

Still constructing a std::vector from a data array requires the former copying the latter – this is inefficient and totally needless.

You might instead base the vector variant on the C array version instead (add(vector.data(), vector.size())) – or to remain fully flexible create an iterator or range based variant that would be fine for any other data type as well:

// add.h

#ifndef SOME_PREFIX_ADD_H_
#define SOME_PREFIX_ADD_H_

#ifdef __cplusplus
// we won't even need C++ headers here...

template <typename Iterator>
double add(Iterator begin, Iterator end)
{
    double result = 0;
    for(; begin != end; ++begin)
    {
        result += *begin;
    }
}
// actually we wouldn't even need that one, we could use
// std::accumulate or here even std::reduce instead...
// you might want to prefer internally:
//{
//    return std::accumulate(begin, end, 0.0);
//}

template <typename Container>
double add(Container& c)
{
    return add(c.begin(), c.end());
}
// this one is fine for std::vector, but actually for most of the other standard
// containers as well, apart from the associative ones

extern "C"
#endif
double add(double const* data, size_t length);
#endif

While a C++ compiler would be fine with having the implementation in the header as well, a C compiler must not see it, as it uses C++ features, so you need to provide the implementation in a separate source file:

// add.cpp

double add(double const* data, size_t length);
{
    return add(data, data + length); // calls the iterator template
}

Note about overloading: On compiling under the hoods C++ 'decorates' function names with a prefix (mainly encoding function arguments) which makes overloads distinguishable one from another. C does not provide such decoration; thus in C, there are no overloads possible (actually still some minimal decoration exists comprising the calling convention; doesn't change anything about not supporting overloading). This means that you can in C++ have an arbitrary number of decorated overloads (i.e. with C++ linkage) and additionally at most one undecorated one (i.e. with C linkage) – which is the case above, too (each template instantiation maps to its own decorated function).

英文:

There's a fundamental problem:

C doesn't know about std::vector – it even doesn't know about templates and neither about namespaces. So any code containing scope resolution (::) or template instantiations (SomeTemplate&lt;SomeType&gt; or similar) won't compile in C. So

double add_raw(std::vector&lt;double&gt; vec)

is not a valid signature in C, and it is not (while legal) meaningful to provide such functions with C-linkage (i.e. applying extern &quot;C&quot; on them).

Additionally: C doesn't know about extern &quot;C&quot; either.

So you need to hide all of these C++ features away from a C compiler! You can so by relying on __cplusplus being defined:

#ifdef __cplusplus
// so this is a C++ build!

// only include C++ headers within this section!
#include &lt;vector&gt;

double add/*_raw*/(std::vector&lt;double&gt;const&amp; data);
//        ^^^^^^^^
// you now even could have an overload, if you wanted to...

extern &quot;C&quot;
#endif
double add(double const* data, size_t length);

Still constructing a std::vector from a data array requires the former copying the latter – this is inefficient and totally needless.

You might instead base the vector variant on the C array version instead (add(vector.data(), vector.size())) – or to remain fully flexible create an iterator or range based variant that would be fine for any other data type as well:

// add.h

#ifndef SOME_PREFIX_ADD_H_
#define SOME_PREFIX_ADD_H_

#ifdef __cplusplus
// we won&#39;t even need C++ headers here...

template &lt;typename Iterator&gt;
double add(Iterator begin, Iterator end)
{
    double result = 0;
    for(; begin != end; ++begin)
    {
        result += *begin;
    }
}
// actually we would&#39;t even need that one, we could use
// std::accumulate or here even std::reduce instead...
// you might want to prefer internally:
//{
//    return std::accumulate(begin, end, 0.0);
//}

template &lt;typename Container&gt;
double add(Container&amp; c)
{
    return add(c.begin(), c.end());
}
// this one is fine for std::vector, but actually for most of the other standard
// containers as well, apart from the associative ones

extern &quot;C&quot;
#endif
double add(double const* data, size_t length);
#endif

While a C++ compiler would be fine with having the implementation for in the header as well a C compiler must not see it, as it uses C++ features, so you need to provide the implementation in a separate source file:

// add.cpp

double add(double const* data, size_t length);
{
    return add(data, data + length); // calls the iterator template
}

Note about overloading: On compiling under the hoods C++ 'decorates' function names with a prefix (mainly encoding function arguments) which makes overloads distinguishable one from another. C does not provide such decoration thus in C there are no overloads possible (actually still some minimal decoration exists comprising the calling convention; doesn't change anything about not supporting overloading). This means that you can in C++ have arbitrary number of decorated overloads (i.e. with C++ linkage) and additionally at most one undecorated one (i.e. with C linkage) – which is the case above, too (each template instantiation maps to its own decorated function).

答案3

得分: 0

问题是在搜索了几天后,通过将GCC编译器中的三个文件libgcc_s_seh-1.dlllibstdc++-6.dlllibwinpthread-1.dll复制到libAddDll.dll文件所在的目录中解决了。

英文:

After several days of searching, the problem was solved by copying the three files libgcc_s_seh-1.dll, libstdc++-6.dll, and libwinpthread-1.dll from the GCC compiler to the directory where the libAddDll.dll file is located.

huangapple
  • 本文由 发表于 2023年6月12日 20:19:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76456615.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定