Skip to content

Commit

Permalink
Refactor code to use abstract classes and PIMPL (#370)
Browse files Browse the repository at this point in the history
* Refactor code to use abstract classes and PIMPL

This paves the way towards having multiple implementations at runtime. It also centralized
the defintions of the "base" or "implementation" classes (such as AdapterBase, PeripheralBase) so
that by explicitly deriving from these ABCs the interfaces of each implemetations are correct.

The public interface for "frontend" classes (the ones used by the library user) are unchanged. The main
differences are:

- They now include an "internal_" shared pointer (the PIMPL)
- Some logic is implemented in the frontend (i.e. checking that a peripheral is connected before
  invoking read_inner, write_inner, etc).
- The logic to detect if the instance is initialized is centralized into a private "operator->()"
- The "safe" classes no longer inherit from the non safe ones. They are castable, though, so most code
  should continue working unmodified.

An additional concept, with the associated classes is introduced, the "Backend". For the time being
this is not exposed in the public API. The idea is that all static methods in the Adapter will be moved
to the Backend as non-static methods and the Backend frontend class will have a static get_backends().
Note that in many cases having a "BackendIMPL.h" header is unncecessary and thus ommited.

* fixup! address review comments.

* fixup! actually add the changes to the commit doh!

* fixup! fixup2
  • Loading branch information
jcarrano authored Dec 20, 2024
1 parent 0ec3925 commit 24cb619
Show file tree
Hide file tree
Showing 63 changed files with 1,687 additions and 1,129 deletions.
20 changes: 8 additions & 12 deletions simpleble/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ set(SIMPLEBLE_SRC
${CMAKE_CURRENT_SOURCE_DIR}/src/frontends/base/Service.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/frontends/base/Characteristic.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/frontends/base/Descriptor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/frontends/base/Backend.cpp

${CMAKE_CURRENT_SOURCE_DIR}/src/backends/common/ServiceBase.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/common/CharacteristicBase.cpp
Expand Down Expand Up @@ -143,7 +144,7 @@ endif()
list(APPEND PRIVATE_COMPILE_DEFINITIONS SIMPLEBLE_LOG_LEVEL=SIMPLEBLE_LOG_LEVEL_${SIMPLEBLE_LOG_LEVEL})
list(APPEND PRIVATE_COMPILE_DEFINITIONS SIMPLEBLE_VERSION="${PROJECT_VERSION}")

if(NOT SIMPLEBLE_TEST)
if(NOT SIMPLEBLE_TEST AND NOT SIMPLEBLE_PLAIN)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(SIMPLEBLE_BACKEND_LINUX ON)
else()
Expand Down Expand Up @@ -186,7 +187,8 @@ if(SIMPLEBLE_PLAIN)

target_sources(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/plain/AdapterPlain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/plain/PeripheralPlain.cpp)
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/plain/PeripheralPlain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/plain/BackendPlain.cpp)
elseif(SIMPLEBLE_BACKEND_LINUX)
message(STATUS "Linux Host Detected")

Expand Down Expand Up @@ -282,11 +284,10 @@ elseif(SIMPLEBLE_BACKEND_WINDOWS)
list(APPEND PRIVATE_COMPILE_OPTIONS "/W1")
endif()

target_include_directories(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/windows)
target_sources(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/windows/AdapterWindows.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/windows/PeripheralWindows.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/windows/BackendWinRT.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/windows/Utils.cpp)

elseif(SIMPLEBLE_BACKEND_MACOS)
Expand All @@ -295,14 +296,13 @@ elseif(SIMPLEBLE_BACKEND_MACOS)
list(APPEND PRIVATE_COMPILE_OPTIONS -fobjc-arc)

target_link_libraries(simpleble PUBLIC "-framework Foundation" "-framework CoreBluetooth" ObjC)
target_include_directories(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos)
target_sources(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/Utils.mm
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/AdapterMac.mm
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/AdapterBaseMacOS.mm
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/PeripheralMac.mm
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/PeripheralBaseMacOS.mm)
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/PeripheralBaseMacOS.mm
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/macos/BackendCoreBluetooth.mm)

set_property(TARGET simpleble PROPERTY INSTALL_RPATH @loader_path)
set_property(TARGET simpleble-c PROPERTY INSTALL_RPATH @loader_path)
Expand All @@ -319,11 +319,10 @@ elseif(SIMPLEBLE_BACKEND_ANDROID)

# Specify the source files for the Android backend
# NOTE: These files have been commented out and replaced by the PLAIN version in order to develop the Android wrapper code.
target_include_directories(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android)
target_sources(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/AdapterAndroid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/PeripheralAndroid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/BackendAndroid.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/android/BluetoothDevice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/android/BluetoothGatt.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/android/BluetoothGattService.cpp
Expand All @@ -335,9 +334,6 @@ elseif(SIMPLEBLE_BACKEND_ANDROID)
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/android/bridge/ScanCallback.cpp
)

target_include_directories(simpleble PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/backends/plain)

target_link_libraries(simpleble PUBLIC
android
nativehelper
Expand Down
26 changes: 25 additions & 1 deletion simpleble/include/simpleble/Adapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,30 @@ namespace SimpleBLE {

class AdapterBase;

/**
* Bluetooth Adapter.
*
* A default-constructed Adapter object is not initialized. Calling any method
* other than `initialized()` on an uninitialized Adapter will throw an exception
* of type `SimpleBLE::NotInitialized`.
*
* NOTE: This class is intended to be used by the user only. Library developers
* should use shared pointers to `AdapterBase` instead.
*/
class SIMPLEBLE_EXPORT Adapter {
public:
Adapter() = default;
virtual ~Adapter() = default;

bool initialized() const;

/**
* Retrieve the underlying OS object/handle.
*
* For certain compatibility with external libraries, we sometimes need to
* expose the actual OS handle to the user. This is particularly important
* for MacOS right now.
*/
void* underlying() const;

std::string identifier();
Expand All @@ -42,11 +60,17 @@ class SIMPLEBLE_EXPORT Adapter {
static bool bluetooth_enabled();

/**
* Fetches a list of all available adapters.
* Fetches a list of all available adapters from all available backends.
*
* This will cause backends to be instantiated/initialized and adapters
* too.
*/
static std::vector<Adapter> get_adapters();

protected:
AdapterBase* operator->();
const AdapterBase* operator->() const;

std::shared_ptr<AdapterBase> internal_;
};

Expand Down
21 changes: 20 additions & 1 deletion simpleble/include/simpleble/AdapterSafe.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,25 @@

#include <simpleble/Adapter.h>
#include <simpleble/PeripheralSafe.h>
#include <memory>
#include <optional>
#include <vector>

namespace SimpleBLE {

namespace Safe {

class SIMPLEBLE_EXPORT Adapter : public SimpleBLE::Adapter {
/**
* Wrapper around the Adapter class that provides a noexcept interface.
*
* We use instances of this class directly and not through shared_ptr because
* this is just a wrapper around the Adapter class, which is already managed by
* shared_ptr.
*/
class SIMPLEBLE_EXPORT Adapter {
public:
Adapter(SimpleBLE::Adapter& adapter);
Adapter(SimpleBLE::Adapter&& adapter);
virtual ~Adapter() = default;

std::optional<std::string> identifier() noexcept;
Expand All @@ -32,6 +43,14 @@ class SIMPLEBLE_EXPORT Adapter : public SimpleBLE::Adapter {

static std::optional<bool> bluetooth_enabled() noexcept;
static std::optional<std::vector<SimpleBLE::Safe::Adapter>> get_adapters() noexcept;

/**
* Cast to the underlying adapter object.
*/
operator SimpleBLE::Adapter() const noexcept;

protected:
SimpleBLE::Adapter internal_;
};

} // namespace Safe
Expand Down
5 changes: 5 additions & 0 deletions simpleble/include/simpleble/Characteristic.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class SIMPLEBLE_EXPORT Characteristic {
Characteristic() = default;
virtual ~Characteristic() = default;

bool initialized() const;

BluetoothUUID uuid();
std::vector<Descriptor> descriptors();
std::vector<std::string> capabilities();
Expand All @@ -28,6 +30,9 @@ class SIMPLEBLE_EXPORT Characteristic {
bool can_indicate();

protected:
CharacteristicBase *operator->();
const CharacteristicBase *operator->() const;

std::shared_ptr<CharacteristicBase> internal_;
};

Expand Down
5 changes: 5 additions & 0 deletions simpleble/include/simpleble/Descriptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ class SIMPLEBLE_EXPORT Descriptor {
Descriptor() = default;
virtual ~Descriptor() = default;

bool initialized() const;

BluetoothUUID uuid();

protected:
DescriptorBase* operator->();
const DescriptorBase* operator->() const;

std::shared_ptr<DescriptorBase> internal_;
};

Expand Down
5 changes: 5 additions & 0 deletions simpleble/include/simpleble/Peripheral.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ class SIMPLEBLE_EXPORT Peripheral {
std::vector<Service> services();
std::map<uint16_t, ByteArray> manufacturer_data();

/* Calling any of the methods below when the device is not connected will throw
Exception::NotConnected */
// clang-format off
ByteArray read(BluetoothUUID const& service, BluetoothUUID const& characteristic);
void write_request(BluetoothUUID const& service, BluetoothUUID const& characteristic, ByteArray const& data);
Expand All @@ -71,6 +73,9 @@ class SIMPLEBLE_EXPORT Peripheral {
void set_callback_on_disconnected(std::function<void()> on_disconnected);

protected:
PeripheralBase* operator->();
const PeripheralBase* operator->() const;

std::shared_ptr<PeripheralBase> internal_;
};

Expand Down
12 changes: 11 additions & 1 deletion simpleble/include/simpleble/PeripheralSafe.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <memory>
#include <optional>

#include <simpleble/export.h>
Expand All @@ -11,9 +12,10 @@ namespace SimpleBLE {

namespace Safe {

class SIMPLEBLE_EXPORT Peripheral : public SimpleBLE::Peripheral {
class SIMPLEBLE_EXPORT Peripheral {
public:
Peripheral(SimpleBLE::Peripheral& peripheral);
Peripheral(SimpleBLE::Peripheral&& peripheral);
virtual ~Peripheral() = default;

std::optional<std::string> identifier() noexcept;
Expand Down Expand Up @@ -47,6 +49,14 @@ class SIMPLEBLE_EXPORT Peripheral : public SimpleBLE::Peripheral {

bool set_callback_on_connected(std::function<void()> on_connected) noexcept;
bool set_callback_on_disconnected(std::function<void()> on_disconnected) noexcept;

/**
* Get the underlying peripheral object.
*/
operator SimpleBLE::Peripheral() const noexcept;

protected:
SimpleBLE::Peripheral internal_;
};

} // namespace Safe
Expand Down
5 changes: 5 additions & 0 deletions simpleble/include/simpleble/Service.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ class SIMPLEBLE_EXPORT Service {
Service() = default;
virtual ~Service() = default;

bool initialized() const;

BluetoothUUID uuid();
ByteArray data();
std::vector<Characteristic> characteristics();

protected:
const ServiceBase* operator->() const;
ServiceBase* operator->();

std::shared_ptr<ServiceBase> internal_;
};

Expand Down
10 changes: 9 additions & 1 deletion simpleble/src/CommonUtils.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#pragma once

#include <algorithm>
#include <vector>

#include "LoggingInternal.h"

#define SAFE_CALLBACK_CALL(cb, ...) \
Expand Down Expand Up @@ -50,4 +53,9 @@ auto values(const MAP& map) {
return ValueCollector<MAP>{map};
}

} // namespace SimpleBLE::util
} // namespace SimpleBLE::Util

namespace SimpleBLE {
template <typename T>
using SharedPtrVector = std::vector<std::shared_ptr<T>>;
}
Loading

0 comments on commit 24cb619

Please sign in to comment.