OpenBluetoothToolbox / SimpleBLE

SimpleBLE - the all-in-one Bluetooth library for MacOS, iOS, Windows, Linux and Android.
https://www.simpleble.org
Other
655 stars 110 forks source link

Rewrite to allow GCC Compilation on Windows #246

Closed ZaneL closed 1 month ago

ZaneL commented 1 year ago

Hello,

I'm using CMAKE to build your library and link it into my project. In Windows, if I use MSVC, it works fine. Out of curiosity, I tried to use G++ (mingw) on Windows and I encountered a few problems.

For some reason, these lines were being overridden by simpleble's CMAKE files, and it kept reverting back to an earlier C++ standard that doesn't have co-routine support. As a result, the winrt header files wouldn't compile.

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

I added this line which fixed the problem target_compile_features(simpleble PUBLIC cxx_std_20)

Then some compilation errors started popping up, for example, I had to change this

GattDescriptor PeripheralBase::_fetch_descriptor(const BluetoothUUID& service_uuid, const BluetoothUUID& characteristic_uuid, const BluetoothUUID& descriptor_uuid);

To this GattDescriptor _fetch_descriptor(const BluetoothUUID& service_uuid, const BluetoothUUID& characteristic_uuid, const BluetoothUUID& descriptor_uuid);

And I had to add const in a few places like this:

Here-->for (const auto& service_guid : service_data) {
                std::string service_uuid = guid_to_uuid(service_guid);
                data.service_data.emplace(std::make_pair(service_uuid, ByteArray()));
             }

Then I encountered more problems because "roapi.h" was being included, and there are MSVC specific compiler macros that are needed to get this thing to build

#pragma region Desktop Family
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    RO_INIT_SINGLETHREADED     = 0,      // Single-threaded application
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    RO_INIT_MULTITHREADED      = 1,      // COM calls objects on any thread.
} RO_INIT_TYPE;

Anyway, I think this project could build with mingw on windows if some stuff was modified a bit. Just wondering what your thoughts are...

ZaneL commented 1 year ago

For example, what is all this for?

void initialize_winrt() {
    static bool initialized = false;

    if (initialized) return;
    initialized = true;

    int32_t cotype, qualifier, get_apartment_result;

    get_apartment_result = WINRT_IMPL_CoGetApartmentType(&cotype, &qualifier);
    SIMPLEBLE_LOG_INFO(fmt::format("CoGetApartmentType: cotype={}, qualifier={}, result={:X}", cotype, qualifier,
                                   (uint32_t)get_apartment_result));

    if (cotype == APTTYPE_STA || cotype == APTTYPE_MAINSTA) {
        SIMPLEBLE_LOG_WARN("Single-threaded apartment detected, uninitializing.");
        RoUninitialize();
    }

    winrt::hresult result = RoInitialize(RO_INIT_MULTITHREADED);
    SIMPLEBLE_LOG_INFO(fmt::format("RoInitialize: result={:X}", (uint32_t)result));
}

Can't you just do this?

//The call to winrt::init_apartment initializes the thread in the Windows Runtime; by default, in a multithreaded apartment. The call also initializes COM.
winrt::init_apartment();
ronaaron commented 1 year ago

I'd like to add my thumbs-up to seeing a MingW static library in the distribution. I tried building with my TDS version of mingw64, but the compiler is quite old (and yes, I need to use that compiler).

kdewald commented 1 month ago

Hey @ZaneL and @ronaaron, thanks for reaching out.

Unfortunately, MinGW won't work as all of the OS-specific APIs are strongly tied to MSVC. One can try to pick the individual files that are consumed and manually piece it together, but it becomes brittle very quickly.

If you want to use SimpleBLE as part of MinGW project, I'd recommend precompiling and linking as a shared library.

Now, answering this question:

For example, what is all this for?

void initialize_winrt() {
    static bool initialized = false;

    if (initialized) return;
    initialized = true;

    int32_t cotype, qualifier, get_apartment_result;

    get_apartment_result = WINRT_IMPL_CoGetApartmentType(&cotype, &qualifier);
    SIMPLEBLE_LOG_INFO(fmt::format("CoGetApartmentType: cotype={}, qualifier={}, result={:X}", cotype, qualifier,
                                   (uint32_t)get_apartment_result));

    if (cotype == APTTYPE_STA || cotype == APTTYPE_MAINSTA) {
        SIMPLEBLE_LOG_WARN("Single-threaded apartment detected, uninitializing.");
        RoUninitialize();
    }

    winrt::hresult result = RoInitialize(RO_INIT_MULTITHREADED);
    SIMPLEBLE_LOG_INFO(fmt::format("RoInitialize: result={:X}", (uint32_t)result));
}

Can't you just do this?

//The call to winrt::init_apartment initializes the thread in the Windows Runtime; by default, in a multithreaded apartment. The call also initializes COM.
winrt::init_apartment();

This is because WinRT crashes if the apartments are not correctly initialized, and depending on the environment where SimpleBLE is used, reinitialization is required. We've had multiple crashes in the past because of this.