Sté*_*ent 4 c++ haskell haskell-ffi
我已将“NumericalIntegration”C++ 库封装在 Haskell 中。这是该软件包的最新版本(Hackage 上的版本较旧)。
这是 C++ 代码的主要部分:
class Integrand {
private:
std::function<double(double)> f;
public:
Integrand(std::function<double(double)>& f_) : f(f_) {}
double operator()(const double x) const { return f(x); }
};
double integration(double f(double),
double lower,
double upper,
double relError,
int subdiv,
double* errorEstimate,
int* errorCode) {
// Define the integrand.
std::function<double(double)> f_ = [&](double x) { return f(x); };
Integrand integrand(f_);
// Define the integrator.
Eigen::Integrator<double> integrator(subdiv);
// Define a quadrature rule.
Eigen::Integrator<double>::QuadratureRule rule =
Eigen::Integrator<double>::GaussKronrod201;
// Define the desired absolute error.
double absError = 0.0;
// Integrate.
double result = integrator.quadratureAdaptive(integrand, lower, upper,
absError, relError, rule);
*errorEstimate = integrator.estimatedError();
*errorCode = integrator.errorCode();
return result;
}
Run Code Online (Sandbox Code Playgroud)
这是 Haskell 代码的主要部分:
foreign import ccall safe "wrapper" funPtr
:: (Double -> Double) -> IO (FunPtr (Double -> Double))
foreign import ccall safe "integration" c_integration
:: FunPtr (Double -> Double) -> Double -> Double -> Double -> Int
-> Ptr Double -> Ptr Int -> IO Double
-- | Numerical integration.
integration :: (Double -> Double) -- ^ integrand
-> Double -- ^ lower bound
-> Double -- ^ upper bound
-> Double -- ^ desired relative error
-> Int -- ^ number of subdivisions
-> IO IntegralResult -- ^ value, error estimate, error code
integration f lower upper relError subdiv = do
errorEstimatePtr <- mallocBytes (sizeOf (0 :: Double))
errorCodePtr <- mallocBytes (sizeOf (0 :: Int))
fPtr <- funPtr f
result <-
c_integration fPtr lower upper relError subdiv errorEstimatePtr errorCodePtr
errorEstimate <- peek errorEstimatePtr
errorCode <- peek errorCodePtr
let out = IntegralResult {_value = result, _error = errorEstimate, _code = errorCode}
free errorEstimatePtr
free errorCodePtr
freeHaskellFunPtr fPtr
return out
Run Code Online (Sandbox Code Playgroud)
这可行,但存在有关集成错误代码的问题。当集成正常时,错误代码应该是 0。有时它是 0,正如预期的那样。但有时它是一个巨大的整数,毫无意义,尽管积分很好。
您对这个问题有什么想法吗?为什么会出现这个奇怪的错误代码?我的代码中有什么不好的地方吗?我不太精通 C++(也不精通 Haskell)。但除了这个奇怪的错误代码之外,该库似乎运行得很好。
在该x86_64平台上,HaskellInt是 64 位,而 Cint是 32 位。(AClong是 64 位。)在您的代码中,您会在高字节中拾取垃圾,并得到一个大得离谱的 64 位整数,毫无疑问,其最低 32 位为零。
无论如何,有一个Foreign.C包含 newtypes CInt、CDouble等的模块,旨在匹配目标平台上相应的 C 类型,我认为始终使用这些被认为是良好的做法:
import Foreign
import Foreign.C
foreign import ccall safe "wrapper" funPtr
:: (CDouble -> CDouble) -> IO (FunPtr (CDouble -> CDouble))
foreign import ccall safe "integration" c_integration
:: FunPtr (CDouble -> CDouble) -> CDouble -> CDouble -> CDouble -> CInt
-> Ptr CDouble -> Ptr CInt -> IO CDouble
Run Code Online (Sandbox Code Playgroud)
当然,由于这些是新类型,因此存在包装和展开的麻烦,尽管通常fromIntegral会自动处理这一点,同时也会跨位大小进行转换:
errorCode <- fromIntegral <$> peek errorCodePtr
Run Code Online (Sandbox Code Playgroud)
但是,作为一种不太可移植的替代方案,您可以在声明中坚持使用Double和,前提是您仅针对 C为 32 位的平台。Int32foreign importint
另请注意,如果类型正确,那么malloc是 的类型安全替代方案mallocBytes:
errorCodePtr <- malloc
Run Code Online (Sandbox Code Playgroud)
的类型Ptr决定了此处分配的正确字节数。