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

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

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:

  1. #pragma once
  2. #include <iostream>
  3. #include <vector>
  4. extern "C" double add(double *arr, int n);
  5. extern "C" double add_raw(std::vector<double> vec);

In the add.cpp:

  1. #include "add.h"
  2. double add(double *arr, int n)
  3. {
  4. std::vector<double> vec(arr, arr + n);
  5. return add_raw(vec);
  6. }
  7. double add_raw(std::vector<double> vec)
  8. {
  9. double sum{};
  10. // Here is some algorithm developed for vector<double>!!!
  11. return sum;
  12. }

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

In the test.py:

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

But I get the following error:

  1. Traceback (most recent call last):
  2. File "f:\testcpp\c++\TestExternalCpp\Test\test1.py", line 11, in <module>
  3. my_dll = ctypes.cdll.LoadLibrary("./dlls/libAddDll.dll")
  4. File "D:\Python310\lib\ctypes\__init__.py", line 452, in LoadLibrary
  5. return self._dlltype(name)
  6. File "D:\Python310\lib\ctypes\__init__.py", line 374, in __init__
  7. self._handle = _dlopen(self._name, mode)
  8. 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:

  1. #pragma once
  2. #include &lt;iostream&gt;
  3. #include &lt;vector&gt;
  4. extern &quot;C&quot; double add(double *arr, int n);
  5. extern &quot;C&quot; double add_raw(std::vector&lt;double&gt; vec);

In the add.cpp:

  1. #include &quot;add.h&quot;
  2. double add(double *arr, int n)
  3. {
  4. std::vector&lt;double&gt; vec(arr, arr + n);
  5. return add_raw(vec);
  6. }
  7. double add_raw(std::vector&lt;double&gt; vec)
  8. {
  9. double sum{};
  10. //here some algrothm developed by vector&lt;double&gt;!!!
  11. return sum;
  12. }

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

In the test.py:

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

But I get the following error:

  1. Traceback (most recent call last):
  2. File &quot;f:\testcpp\c++\TestExternalCpp\Test\test1.py&quot;, line 11, in &lt;module&gt;
  3. my_dll = ctypes.cdll.LoadLibrary(&quot;./dlls/libAddDll.dll&quot;)
  4. File &quot;D:\Python310\lib\ctypes\__init__.py&quot;, line 452, in LoadLibrary
  5. return self._dlltype(name)
  6. File &quot;D:\Python310\lib\ctypes\__init__.py&quot;, line 374, in __init__
  7. self._handle = _dlopen(self._name, mode)
  8. 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:

  1. #ifndef ADD_H
  2. #define ADD_H
  3. #ifdef __cplusplus
  4. // C++ code
  5. #include <vector>
  6. double add_raw(std::vector<double> vec);
  7. // Introduce the functions that can be used either in C or C++
  8. extern "C"
  9. {
  10. #endif
  11. // C or C++ code
  12. double add(double *arr, size_t n);
  13. #ifdef __cplusplus
  14. } // Close the extern "C" block
  15. #endif
  16. #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:

  1. #ifndef ADD_H
  2. #define ADD_H
  3. #ifdef __cplusplus
  4. // C++ code
  5. #include &lt;vector&gt;
  6. double add_raw(std::vector&lt;double&gt; vec);
  7. // Introduce the functions that can be used either in C or C++
  8. extern &quot;C&quot;
  9. {
  10. #endif
  11. // C or C++ code
  12. double add(double *arr, size_t n);
  13. #ifdef __cplusplus
  14. } // Close the extern &quot;C&quot; block
  15. #endif
  16. #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:

  1. #ifdef __cplusplus
  2. // so this is a C++ build!
  3. // only include C++ headers within this section!
  4. #include <vector>;
  5. double add/*_raw*/(std::vector<double> const& data);
  6. // ^^^^^^^^
  7. // you now even could have an overload, if you wanted to...
  8. extern "C"
  9. #endif
  10. 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:

  1. // add.h
  2. #ifndef SOME_PREFIX_ADD_H_
  3. #define SOME_PREFIX_ADD_H_
  4. #ifdef __cplusplus
  5. // we won't even need C++ headers here...
  6. template <typename Iterator>
  7. double add(Iterator begin, Iterator end)
  8. {
  9. double result = 0;
  10. for(; begin != end; ++begin)
  11. {
  12. result += *begin;
  13. }
  14. }
  15. // actually we wouldn't even need that one, we could use
  16. // std::accumulate or here even std::reduce instead...
  17. // you might want to prefer internally:
  18. //{
  19. // return std::accumulate(begin, end, 0.0);
  20. //}
  21. template <typename Container>
  22. double add(Container& c)
  23. {
  24. return add(c.begin(), c.end());
  25. }
  26. // this one is fine for std::vector, but actually for most of the other standard
  27. // containers as well, apart from the associative ones
  28. extern "C"
  29. #endif
  30. double add(double const* data, size_t length);
  31. #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:

  1. // add.cpp
  2. double add(double const* data, size_t length);
  3. {
  4. return add(data, data + length); // calls the iterator template
  5. }

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

  1. 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:

  1. #ifdef __cplusplus
  2. // so this is a C++ build!
  3. // only include C++ headers within this section!
  4. #include &lt;vector&gt;
  5. double add/*_raw*/(std::vector&lt;double&gt;const&amp; data);
  6. // ^^^^^^^^
  7. // you now even could have an overload, if you wanted to...
  8. extern &quot;C&quot;
  9. #endif
  10. 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:

  1. // add.h
  2. #ifndef SOME_PREFIX_ADD_H_
  3. #define SOME_PREFIX_ADD_H_
  4. #ifdef __cplusplus
  5. // we won&#39;t even need C++ headers here...
  6. template &lt;typename Iterator&gt;
  7. double add(Iterator begin, Iterator end)
  8. {
  9. double result = 0;
  10. for(; begin != end; ++begin)
  11. {
  12. result += *begin;
  13. }
  14. }
  15. // actually we would&#39;t even need that one, we could use
  16. // std::accumulate or here even std::reduce instead...
  17. // you might want to prefer internally:
  18. //{
  19. // return std::accumulate(begin, end, 0.0);
  20. //}
  21. template &lt;typename Container&gt;
  22. double add(Container&amp; c)
  23. {
  24. return add(c.begin(), c.end());
  25. }
  26. // this one is fine for std::vector, but actually for most of the other standard
  27. // containers as well, apart from the associative ones
  28. extern &quot;C&quot;
  29. #endif
  30. double add(double const* data, size_t length);
  31. #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:

  1. // add.cpp
  2. double add(double const* data, size_t length);
  3. {
  4. return add(data, data + length); // calls the iterator template
  5. }

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:

确定