C++ API in Purebasic nutzen

Fragen zu allen anderen Programmiersprachen.
Benutzeravatar
Macros
Beiträge: 1314
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

C++ API in Purebasic nutzen

Beitrag von Macros »

Ich hab ein Projekt, bei dem ich ein Gerät verwenden muss für das der Hersteller nur eine C++ API anbietet. Wie stelle ich das am besten an?

Ideen, die mir kamen:
- Eine DLL mit C++ aus den API Funktionen erstellen (wie gehe ich mit Objekten um?)
- Die API Datei zerlegen und ei Aufrufe an die zugrunde liegende DLL herauspicken
- Eine bisschen Code in C++ schreiben und eine DLL bauen, die PB kompatible Werte zurückgibt
(Muss ich beim Aufruf etwas beachten?)
- ...?
Vor allem stellt sich die Frage: Wie baue ich am besten mit C++ eine DLL für PureBasic?

Bereits gelernt: Nächstes mal besser darauf achten, welches Gerät mir vorgelegt wird ;)

Für die Interessierten:
Gerät: CEBO-LC 16-bit Multi-I/O USB measurement laboratory
Programming Reference
API Download

Und ein Codebeispiel mit deren API:

Code: Alles auswählen

/**
 * CeboMsr - C++ example - Single I/O.
 *
 * The I/O's of digital port #0 are read and mirrored to digital port #1.
 * (IO-0 - IO-7 are mirrored to IO-8 - IO-15).
 * The voltage at AI-0 is mirrored to AO-1 and AI-1 to AO-0 too.
 *
 * Does only work on CeboLC devices.
 *
 * (c) 2015 Cesys GmbH
 * @author Thomas Hoppe
 */

#include <iostream>
#include <stdexcept>

#include <cebomsrpp.h>

using namespace std;
using namespace CeboMsr;

/**
 * Do very simple IO, digital as well as analog.
 */
void singleIoTest(Device &device) {
	// Read digital port #0 and write result to digital port #1 (mirror pins).
	// At first, we need references to both ports.
	// Port #0 will be a copy of the original instance.
	DigitalPort dp0 = device.getDigitalPorts().at(0);

	// As an alternative, get a reference to Port #1.
	const DigitalPort &dp1 = device.getDigitalPorts().at(1);

	// Configure port first, all bits of digital port #0 as input (default)
	// and all bits of digital port #1 as output.
	dp1.setOutputEnableMask(0xff);

	// Read from #0 ...
	int value = dp0.read();

	// Write the value to #1 ...
	dp1.write(value);

	// Now some analog I/O, do it without any additional local references.
	// Read single ended #0 input voltage ...
	float vvalue = device.getSingleEndedInputs().at(0).read();

	// Write value to analog output #1
	device.getAnalogOutputs().at(1).write(vvalue);

	// .. or in one line: single ended #1 to analog output #0 ...
	device.getAnalogOutputs().at(0).write(device.getSingleEndedInputs().at(1).read());

	// Reset to power up defaults.
	device.resetDevice();
}

/**
 * Application entry point.
 */
int main() {
	// Nearly all methods can raise exceptions. For this example, the error handling is done at this single point.
	try {
		// Search for devices ...
		DeviceVector devices = LibraryInterface::enumerate(DeviceType::CeboLC);

		// If at least one has been found, use the first one ...
		if (!devices.empty()) {
			Device device = devices[0];

			// Open device, nothing can be done without doing this.
			device.open();

			// *** DIGITAL PORTS AND ANALOG OUTPUTS ARE MODIFIED DURING THIS ***
			// *** SECTION, SO DO NOT CONNECT ANY PERIPHERALS WITHOUT STUDYING ***
			// *** THE ACTIONS DONE HERE, OTHERWISE YOUR PERIPHERALS MAY BE DAMAGED !!! ***
			singleIoTest(device);

			// Finalize device usage, this free's up the device, so it can be used
			// again, including other applications.
			device.close();
		}
	} // These exceptions are currently used to report different problems ...
	catch (const std::invalid_argument &e) { cout << "Invalid argument exception raised: " << e.what() << endl; }
	catch (const std::out_of_range &e) { cout << "Out of range exception raised: " << e.what() << endl; }
	catch (const std::logic_error &e) { cout << "Logic error exception raised: " << e.what() << endl; }
	catch (const std::runtime_error &e) { cout << "Runtime/IO error exception raised: " << e.what() << endl; }
	catch (const std::exception &e) { cout << "Unknown exception raised: " << e.what() << endl; }

	return 0;
}
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: C++ API in Purebasic nutzen

Beitrag von NicTheQuick »

Offenbar enthält die Datei "cebomsr.h" ein ganz normales C-Binding, sodass du ohne Klassen auf jede Funktion zugreifen kannst. Du musst lediglich die Header-Datei in PB übersetzen.

Edit:
Hier ein Beispielcode:

Code: Alles auswählen

EnableExplicit

Macro int
	l
EndMacro

Macro CeboMsrErrorIndex
	int
EndMacro

Macro CeboMsrType
	int
EndMacro

Enumeration CeboMsrErrorIndex
	#SUCCESS
EndEnumeration

Enumeration CeboMsrType
	#CeboMsrTypeAll = $ceb00000
	#CeboMsrTypeUsb = $ceb00001
	#CeboMsrTypeCeboLC = $ceb00010
	#CeboMsrTypeCeboStick = $ceb00011
	#CeboMsrTypeCeboDFN = $ceb00012
EndEnumeration

ImportC "cebomsr-1.6-x86_64.lib"
	; Error handling
	CeboMsrGetErrorLength.int(index.CeboMsrErrorIndex)
	CeboMsrGetError(index.CeboMsrErrorIndex, *buffer)

	; Host version information
	CeboMsrGetApiVersion.CeboMsrErrorIndex(*buffer)
	
	; Enumeration
	CeboMsrBeginEnumerate.CeboMsrErrorIndex(*count.Integer, type.CeboMsrType)
EndImport

; Gib Versionsinformationen aus
Define *buffer = AllocateMemory(1024)
If CeboMsrGetApiVersion(*buffer) = #SUCCESS
	Debug PeekS(*buffer, -1, #PB_Ascii)
EndIf
FreeMemory(*buffer9

; Zeige die Anzahl der erkannten Geräte an
Define count.int
If CeboMsrBeginEnumerate(@count, #CeboMsrTypeAll) = #SUCCESS
	Debug count
EndIf
Die Dateien "cebomsr-1.6-x86_64.dll", "libusb-1.0-x86_64.dll" und "cebomsr-1.6-x86_64.lib" müssen natürlich im selben Verzeichnis wie die EXE-Datei liegen.

Mit den Macros wollte ich nur die Übersichtlichkeit bewahren, indem ich die selben Typennamen wie in "cebomsr.h" benutze.
Bild
Benutzeravatar
Macros
Beiträge: 1314
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: C++ API in Purebasic nutzen

Beitrag von Macros »

Danke, da du hast recht.

Ich Unwissender hab mich von den umbenannten Integer typen täuschen lassen ;)

Auch wenn es dazu keine Dokumentation gibt, werde ich wohl mit etwas probieren und spicken im C++ Teil damit arbeiten können :)
Bild
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8675
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 32 GB DDR4-3200
Ubuntu 22.04.3 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken
Kontaktdaten:

Re: C++ API in Purebasic nutzen

Beitrag von NicTheQuick »

Ich habe oben noch drei Funktionen hinzugefügt und die Version ausgeben lassen, falls es hilft.
Bild
Benutzeravatar
Macros
Beiträge: 1314
Registriert: 23.12.2005 15:00
Wohnort: Olching(bei FFB)
Kontaktdaten:

Re: C++ API in Purebasic nutzen

Beitrag von Macros »

Vielen Dank für dein Beispiel, funktioniert wunderbar :allright:

Wenn nach meinem Projekt etwas Freizeit gefunden hab um den Code zu polieren, stell ich mein Include hier rein.
Allerdings hat der Hersteller schon gesagt: Die DLL/C Bibliothek ist nicht dokumentiert, weil sie die Funktionen in ihr eventuell ändern.
Bild
Antworten