Windows 시스템에서 Ceres 비선형 최적화 라이브러리를 사용합니다. (2) Ceres 라이브러리를 호출합니다.

(1) Ceres 라이브러리 설치

(2) Ceres 라이브러리 호출

        1. DLL 라이브러리 WrapCeres 작성

                1.1 내보낼 코드 작성

                1.2 컴파일 링크 구성

                1.3 컴파일

        2. C#으로 DLL 라이브러리 WrapCeres 호출

                2.1 C#과 C/C++의 타입 대응

                2.2.Dllimport 상세설명

                2.3 C#에서 C++을 호출하는 방법에 대한 심층 논의

                2.4 WPF에서 WrapCeres.dll을 실제로 호출하기

(3) 애플리케이션 릴리스

(2) Ceres 라이브러리 호출

위에서는 helloworld, helloworld_static 등과 같이 Ceres와 함께 제공되는 일부 루틴을 실제로 실행했습니다. 관심이 있으시면 이러한 공식 루틴을 자세히 공부하실 수 있으며 여기서는 많이 말하지 않겠습니다.

 이러한 공식 Ceres 루틴은 응용 프로그램 프로젝트라는 점에 주목할 가치가 있습니다. 즉, 이러한 루틴으로 컴파일된 exe 파일을 직접 실행할 수 있습니다. 간단한 C++ 응용 프로그램을 직접 작성하여 Ceres 라이브러리를 호출하여 몇 가지 간단한 기능을 구현하려는 경우 이러한 공식 루틴에서 자신의 프로그램을 작성하는 방법을 배울 수 있습니다. 그러나 일반적으로 매우 복잡한 Windows 데스크톱 응용 프로그램에서 수학적 계산 작업을 구현하기 위해 Ceres 라이브러리를 호출합니다. Windows 데스크톱 애플리케이션은 일반적으로 WPF 데스크톱 애플리케이션과 같은 .NET 기반 C# 프로그램입니다. 우리는 C++ 응용 프로그램이 다른 C++ 라이브러리(lib 정적 라이브러리 및 dll 동적 라이브러리 모두)를 직접 호출할 수 있는 반면 C# 응용 프로그램은 내보내기를 통해서만 C++ dll 동적 라이브러리를 호출할 수 있다는 것을 알고 있습니다. C# 응용 프로그램이 많은 다른 C++ 라이브러리를 호출해야 하고 이러한 C++ 라이브러리에는 동적 라이브러리와 정적 라이브러리가 모두 포함되어 있으면 더 번거롭습니다. 일반적으로 우리가 사용하고자 하는 이러한 C++ 라이브러리는 특수 C++ DLL에 통합되며, 조건이 허락한다면 이러한 C++ 라이브러리를 이 특수 C++ DLL에 통합하기 위해 정적 링크를 사용하는 것이 가장 좋습니다. Ceres 라이브러리 호출을 예로 들어 이 특별한 C++ DLL을 작성하는 방법과 C#에서 이 특별한 C++ DLL을 호출하는 방법을 소개하겠습니다.

1. DLL 라이브러리 WrapCeres 작성

편의를 위해 WrapCeres라는 위의 ceres-windows 솔루션 바로 아래에 새로운 C++ DLL 빈 프로젝트를 만들었습니다.

 1.1 내보낼 코드 작성

프로젝트에서 새로운 헤더 파일 wrap.h와 소스 파일 wrap.cpp를 생성하고, wrap.h에 내보낼 변수/함수/클래스의 선언을 채우고, 변수/함수/클래스의 정의를 다음과 같이 채웁니다. wrap.cpp 로 내보낼 수 있습니다.

wrap.h 헤더 파일을 작성합니다.

#pragma once

#ifndef _WRAP_H
#define _WRAP_H
extern "C" _declspec(dllexport) int Add(int a, int b);
extern "C" _declspec(dllexport) int nCeresDll;
extern "C" _declspec(dllexport) int fnCeresDll(void);
extern "C" _declspec(dllexport) double testCeresDll(void);
#endif

wrap.cpp 소스 파일을 작성합니다.

#include "pch.h"
#include "wrap.h"
#include "ceres\ceres.h"
#include "glog\logging.h"

using ceres::AutoDiffCostFunction;
using ceres::CostFunction;
using ceres::Problem;
using ceres::Solver;

int Add(int a, int b)
{
	return 2*(a + b);
}



struct CostFunctor {
	template <typename T>
	bool operator()(const T* const x, T* residual) const {
		residual[0] = T(10.0) - x[0];
		return true;
	}
};
// 这是导出变量的一个示例
int nCeresDll = 0;

// 这是导出函数的一个示例。
int fnCeresDll(void)
{
	return 0;
}

double testCeresDll(void)
{
	google::InitGoogleLogging(new char(123));
	// The variable to solve for with its initial value.
	double initial_x = 5.0;
	double x = initial_x;
	// Build the problem.
	Problem problem;
	// Set up the only cost function (also known as residual). This uses
	// auto-differentiation to obtain the derivative (jacobian).
	CostFunction* cost_function =
		new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
	problem.AddResidualBlock(cost_function, NULL, &x);

	// Run the solver!
	Solver::Options options;
	options.linear_solver_type = ceres::DENSE_QR;
	options.minimizer_progress_to_stdout = true;
	Solver::Summary summary;
	Solve(options, &problem, &summary);
	return x;
}

1.2 컴파일 링크 구성

(1) 출력 디렉토리 및 중간 디렉토리

여기서 WrapCeres 프로젝트의 출력 디렉터리와 중간 디렉터리를 솔루션의 다른 프로젝트 구성과 동일하게 설정하여 나중에 다른 종속 라이브러리 파일을 쉽게 찾을 수 있고 게시할 때도 쉽게 찾을 수 있습니다.

(2) 헤더 파일 디렉토리

WrapCeres.cpp는 ceres 및 종속 헤더 파일을 사용하므로 ceres, Eigen 및 glog의 헤더 파일 경로를 지정해야 합니다. 여기에서는 상대 경로가 사용됩니다.

$(프로젝트 디렉터리)\..\Own

$(프로젝트 디렉터리)\..\glog\src\windows

$(ProjectDir)\..\ceres-solver\include

$(ProjectDir)\..\ceres-solver\internal

$(프로젝트 디렉터리)\..\win\include

(3) 매크로

다음 매크로는 전처리기 정의에 추가해야 합니다. 그렇지 않으면 컴파일할 때 오류가 보고됩니다. 추가할 매크로는 다음과 같습니다.

GLOG_NO_ABBREVIATED_SEVERITIES

_CRT_NONSTDC_NO_DEPRECATE

노민맥스

(4) 라이브러리 파일 디렉토리

WrapCeres는 ceres 라이브러리에 의존하기 때문에 컴파일 프로세스는 ceres 라이브러리를 연결해야 하므로 ceres 라이브러리 파일의 경로를 지정해야 하며 여기에서는 상대 경로를 사용합니다.

$(SolutionDir)$(플랫폼)\$(구성)\

동시에 추가 종속성에 특정 라이브러리 파일 이름을 지정하고 다음과 같이 라이브러리 파일 이름을 입력합니다.

세레스.lib

libglog_static.lib

참고로 위의 추가 종속성에서 ceres.lib를 ceres_static.lib로 변경한 후 컴파일 후 오류가 보고되지만 문제는 그리 크지 않습니다. 함께 ceres.dll. . 시스템 수준의 VC++ 런타임 종속성에서 벗어나고 싶다면 위의 방법에 따라 "C++ 런타임을 출력 디렉터리에 복사"를 "예"로 설정한 다음 여러 dll과 자체 WrapCeres.dll을 복사하면 됩니다. 그리고 ceres .dll이 라인에서 함께 전송되었습니다.

1.3 컴파일

위의 구성이 완료되면 프로젝트 WrapCeres를 컴파일할 수 있습니다. 컴파일이 성공적으로 완료되면 다음 단계를 위해 출력 디렉터리에 WrapCeres.dll 및 ceres.dll을 복사할 수 있습니다.

2. C#으로 DLL 라이브러리 WrapCeres 호출

우리는 C++ 응용 프로그램이 다른 C++ 라이브러리(lib 정적 라이브러리 및 dll 동적 라이브러리 모두)를 직접 호출할 수 있는 반면 C# 응용 프로그램은 내보내기를 통해서만 C++ dll 동적 라이브러리를 호출할 수 있다는 것을 알고 있습니다. 이것이 위에서 DLL 동적 라이브러리 WrapCeres를 작성한 이유입니다. 다음은 이 WrapCeres.dll을 C#에서 호출하는 방법을 설명합니다.

2.1 C#과 C/C++의 타입 대응

 2.2.Dllimport 상세설명

C#에서는 DllImport를 사용하여 C++ DLL 라이브러리의 변수/함수/클래스를 호출할 수 있습니다. DllImport 사용법은 다음과 같습니다.

[DllImport("WrapCeres.dll")]

공개 정적 extern int Add(int x,int y);

실제로 DllImport에는 dllName, CallingConvention, CharSet, EntryPoint, ExactSpelling 및 SetLastError를 포함하여 많은 선택적 매개 변수가 있습니다.

  • dllName: 동적 연결 라이브러리 이름
  • CallingConvention: 호출 규칙(C 언어 호출 규칙 및 표준 호출 규칙)
  • CharSet: 문자열 인코딩 형식 설정
  • EntryPoint: 함수 항목 이름, 메서드 자체의 이름이 기본적으로 사용됩니다.
  • ExactSpelling: EntryPoint가 표시된 진입점의 철자와 정확히 일치해야 하는지 여부 기본값은 true, false일 경우 CharSet에 따라 해당 진입 함수의 A 버전 또는 W 버전을 검색한다. 찾을 수 없으며 항목 기능을 찾으십시오.
  • SetLastError: 메서드가 Win32 "마지막 오류"를 유지하는지 여부를 나타냅니다. 기본값이 false인 경우 Win32 오류가 호출자 스레드로 설정되는지 여부에 따라 오류 코드는 C#에서 Marshal.GetLastWin32Error()를 통해 얻을 수 있으며 true로 설정하면 얻을 수 있습니다.

기본 데이터 전달 및 함수 반환 값:

  • 반환 값
  • 주소 참조
  • 포인터 참조
  • Marshal.Read의 구조 분석
  • 구조 분석의 전반적인 읽기

특정 사용 루틴은 다음 문서를 참조하십시오.

C#은 C++를 자세히 호출합니다. - 프로그래머 구함

2.3 C#에서 C++을 호출하는 방법에 대한 심층 논의

여기에서는 C++ 호출, C++ 클래스 호출, C++ 클래스에서 콜백 함수 호출의 일반 함수 및 변수를 소개합니다. 특정 루틴에 대해서는 이 문서를 참조하십시오.

C#에서 호출하는 C++ 라이브러리 사용(3가지 방법)_C#에서 C++ 라이브러리를 참조하는 방법_Milu_Y's Blog-CSDN Blog

(1) C++의 일반 함수 및 변수 호출

DllImport 기능을 사용하여 함수 및 변수를 호출합니다. 예를 들면 다음과 같습니다.

[DllImport("WrapCeres.dll")]

공개 정적 extern int Add(int x,int y);

(2) C++ 클래스 호출

C#은 C++ 클래스 라이브러리에 있는 클래스를 직접 호출할 수 없으므로 다른 C++ 클래스 라이브러리를 생성하여 호출할 클래스 멤버 메서드를 노출하는 유연한 솔루션이 필요합니다.

(3) C++ 클래스에서 콜백 함수 호출

C++의 콜백 함수는 이벤트 응답 메커니즘으로 C#의 위임과 유사합니다.예를 들어 C++ 클래스의 콜백 함수는 개인적인 느낌과 방법이 비슷합니다.

2.4 WPF에서 WrapCeres.dll을 실제로 호출하기

WPF 프로그램에서 WrapCeres.dll의 일부 메서드를 호출해야 하는 경우 DllImport를 사용하여 WrapCeres.dll의 해당 메서드를 로드하면 다음과 같은 위치에서 이러한 프로그램을 편리하게 사용할 수 있습니다.

마지막으로 WPF를 컴파일하여 exe 실행 파일을 생성하고 exe를 WrapCeres.dll 및 ceres.dll과 함께 클라이언트에 보냅니다. exe와 이러한 dll을 통합하여 사용자에게 보낼 수 있는 방법이 있습니까? 이 문제는 아래에서 설명합니다.

또 다른 문제는 C# 프로그램에 대한 시스템 수준 런타임 지원입니다. 시스템 수준 런타임 라이브러리 Virtual C++에 의존해야 하는 위의 C++ 프로그램과 마찬가지로 C# 프로그램도 시스템 수준 런타임 라이브러리 .NET Framework에 의존해야 합니다. 그러나 .NET Framework는 Windows 시스템과 함께 제공되는 기본 구성 요소이며 시스템의 다른 버전은 다음과 같이 다른 버전의 .NET Framework와 함께 제공됩니다.

-win7 .NET Framework3.5(.NET 2.0 및 3.0 포함)

-win10 .NET Framework4.6 이상(.NET Framework3.5는 켜거나 끌 수 있음)

-win11 .NET Framework4.8(.NET Framework3.5는 켜거나 끌 수 있음)

 C# 프로그램이 서로 다른 시스템 버전(일반적으로 win7 및 win10)에서 호환되도록 하려면 사용자가 서로 다른 .NET Framework 버전 환경에서 선택할 수 있도록 여러 exe를 컴파일해야 합니다. 물론 .NET Framework 버전 환경을 변경하면 코드의 일부 종속성, 타사 패키지 및 코드의 개별 문을 수정해야 할 수 있습니다. C# 프로그램에서 .NET Framework 버전을 선택하는 방법은 다음과 같습니다.

참조

[1] Zhang Hu, 로봇 SLAM 내비게이션 핵심 기술 및 실습 [M]. Machinery Industry Press, 2022.

추천

출처blog.csdn.net/m0_68732180/article/details/130234769