英文:
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 <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 some algrothm developed by 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.
答案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 "C"
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 <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
答案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
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<SomeType>
or similar) won't compile in C. So
double add_raw(std::vector<double> 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 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 would'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 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.dll、libstdc++-6.dll和libwinpthread-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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论