<commit_before>/*
 * #%L
 * %%
 * Copyright (C) 2017 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include <boost/algorithm/string/predicate.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/JoynrClusterControllerRuntime.h"
#include "joynr/tests/testProxy.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MulticastSubscriptionQos.h"
#include "joynr/OnChangeSubscriptionQos.h"
#include "joynr/tests/testAbstractProvider.h"
#include "joynr/LibjoynrSettings.h"
#include "joynr/PrivateCopyAssign.h"
#include "joynr/BrokerUrl.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockLocationUpdatedSelectiveFilter.h"
#include "tests/mock/MockSubscriptionListener.h"
#include "tests/utils/MyTestProvider.h"
#include "tests/utils/PtrUtils.h"

using namespace ::testing;
using namespace joynr;

static const std::string messagingPropertiesPersistenceFileName1(
        "End2EndBroadcastTest-runtime1-joynr.persist");
static const std::string messagingPropertiesPersistenceFileName2(
        "End2EndBroadcastTest-runtime2-joynr.persist");

namespace joynr {

class End2EndBroadcastTestBase : public TestWithParam< std::tuple<std::string, std::string> > {
public:
    std::shared_ptr<JoynrClusterControllerRuntime> runtime1;
    std::shared_ptr<JoynrClusterControllerRuntime> runtime2;
    std::string baseUuid;
    std::string uuid;
    std::string domainName;
    Semaphore semaphore;
    Semaphore altSemaphore;
    joynr::tests::TestLocationUpdateSelectiveBroadcastFilterParameters filterParameters;
    std::shared_ptr<MockLocationUpdatedSelectiveFilter> filter;
    std::uint16_t registerProviderWait;
    std::uint16_t subscribeToAttributeWait;
    std::uint16_t subscribeToBroadcastWait;
    joynr::types::Localisation::GpsLocation gpsLocation;
    joynr::types::Localisation::GpsLocation gpsLocation2;
    joynr::types::Localisation::GpsLocation gpsLocation3;
    joynr::types::Localisation::GpsLocation gpsLocation4;

    End2EndBroadcastTestBase() :
        runtime1(),
        runtime2(),
        baseUuid(util::createUuid()),
        uuid( "_" + baseUuid.substr(1, baseUuid.length()-2)),
        domainName("cppEnd2EndBroadcastTest_Domain" + uuid),
        semaphore(0),
        altSemaphore(0),
        filter(std::make_shared<MockLocationUpdatedSelectiveFilter>()),
        registerProviderWait(1000),
        subscribeToAttributeWait(2000),
        subscribeToBroadcastWait(2000),
        gpsLocation(types::Localisation::GpsLocation()),
        gpsLocation2(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         2)),
        gpsLocation3(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         3)),
        gpsLocation4(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         4)),
        providerParticipantId(),
        integration1Settings("test-resources/libjoynrSystemIntegration1.settings"),
        integration2Settings("test-resources/libjoynrSystemIntegration2.settings"),
        httpTransport(false)
    {
        auto settings1 = std::make_unique<Settings>(std::get<0>(GetParam()));
        auto settings2 = std::make_unique<Settings>(std::get<1>(GetParam()));
        MessagingSettings messagingSettings1(*settings1);
        MessagingSettings messagingSettings2(*settings2);
        messagingSettings1.setMessagingPropertiesPersistenceFilename(
                    messagingPropertiesPersistenceFileName1);
        messagingSettings2.setMessagingPropertiesPersistenceFilename(
                    messagingPropertiesPersistenceFileName2);

        std::string brokerProtocol = messagingSettings1.getBrokerUrl().getBrokerChannelsBaseUrl().getProtocol();
        httpTransport = boost::iequals(brokerProtocol, "http") || boost::iequals(brokerProtocol, "https");

        Settings::merge(integration1Settings, *settings1, false);

        runtime1 = std::make_shared<JoynrClusterControllerRuntime>(std::move(settings1));
        runtime1->init();

        Settings::merge(integration2Settings, *settings2, false);

        runtime2 = std::make_shared<JoynrClusterControllerRuntime>(std::move(settings2));
        runtime2->init();

        filterParameters.setCountry("Germany");
        filterParameters.setStartTime("4.00 pm");
        runtime1->start();
        runtime2->start();
    }

    ~End2EndBroadcastTestBase(){
        if (!providerParticipantId.empty()) {
            unregisterProvider();
        }
        bool deleteChannel = true;
        runtime1->stop(deleteChannel);
        runtime2->stop(deleteChannel);
        test::util::resetAndWaitUntilDestroyed(runtime1);
        test::util::resetAndWaitUntilDestroyed(runtime2);

        // Delete persisted files
        test::util::removeAllCreatedSettingsAndPersistencyFiles();
    }

private:
    std::string providerParticipantId;
    Settings integration1Settings;
    Settings integration2Settings;
    DISALLOW_COPY_AND_ASSIGN(End2EndBroadcastTestBase);
    bool httpTransport;

protected:
    bool usesHttpTransport() {
       return httpTransport;
    }

    std::shared_ptr<MyTestProvider> registerProvider() {
        return registerProvider(runtime1);
    }

    void unregisterProvider() {
        return runtime1->unregisterProvider(providerParticipantId);
    }

    std::shared_ptr<MyTestProvider> registerProvider(std::shared_ptr<JoynrClusterControllerRuntime> runtime) {
        auto testProvider = std::make_shared<MyTestProvider>();
        types::ProviderQos providerQos;
        std::chrono::milliseconds millisSinceEpoch =
                std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::system_clock::now().time_since_epoch());
        providerQos.setPriority(millisSinceEpoch.count());
        providerQos.setScope(joynr::types::ProviderScope::GLOBAL);
        providerQos.setSupportsOnChangeSubscriptions(true);
        providerParticipantId = runtime->registerProvider<tests::testProvider>(domainName, testProvider, providerQos);

        // This wait is necessary, because registerProvider is async, and a lookup could occur
        // before the register has finished.
        std::this_thread::sleep_for(std::chrono::milliseconds(registerProviderWait));

        return testProvider;
    }

    std::shared_ptr<tests::testProxy> buildProxy() {
        return buildProxy(runtime2);
    }

    std::shared_ptr<tests::testProxy> buildProxy(std::shared_ptr<JoynrClusterControllerRuntime> runtime) {
        std::shared_ptr<ProxyBuilder<tests::testProxy>> testProxyBuilder
                = runtime->createProxyBuilder<tests::testProxy>(domainName);
        DiscoveryQos discoveryQos;
        discoveryQos.setArbitrationStrategy(DiscoveryQos::ArbitrationStrategy::HIGHEST_PRIORITY);
        discoveryQos.setDiscoveryTimeoutMs(3000);
        discoveryQos.setRetryIntervalMs(250);

        std::int64_t qosRoundTripTTL = 500;

        std::shared_ptr<tests::testProxy> testProxy(testProxyBuilder
                                                   ->setMessagingQos(MessagingQos(qosRoundTripTTL))
                                                   ->setDiscoveryQos(discoveryQos)
                                                   ->build());

        return testProxy;
    }

    template <typename FireBroadcast, typename SubscribeTo, typename UnsubscribeFrom, typename T>
    void testOneShotBroadcastSubscription(const T& expectedValue,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast) {
        auto mockListener = std::make_shared<MockSubscriptionListenerOneType<T>>();

        // Use a semaphore to count and wait on calls to the mock listener
        ON_CALL(*mockListener, onReceive(Eq(expectedValue)))
                .WillByDefault(ReleaseSemaphore(&semaphore));

        testOneShotBroadcastSubscription(mockListener,
                                         subscribeTo,
                                         unsubscribeFrom,
                                         fireBroadcast,
                                         expectedValue);
    }

    template <typename SubscriptionListener,
              typename FireBroadcast,
              typename SubscribeTo,
              typename UnsubscribeFrom,
              typename ...T>
    void testOneShotBroadcastSubscription(SubscriptionListener subscriptionListener,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast,
                                          T... expectedValues) {
        std::vector<std::string> partitions({}); // TODO test with real partitions
        std::shared_ptr<MyTestProvider> testProvider = registerProvider();

        std::shared_ptr<tests::testProxy> testProxy = buildProxy();

        auto subscriptionQos = std::make_shared<MulticastSubscriptionQos>();
        subscriptionQos->setValidityMs(500000);

        std::string subscriptionId;
        subscribeTo(testProxy.get(), subscriptionListener, subscriptionQos, subscriptionId);

        (*testProvider.*fireBroadcast)(expectedValues..., partitions);

        // Wait for a subscription message to arrive
        ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(3)));
        unsubscribeFrom(testProxy.get(), subscriptionId);
    }

    template <typename BroadcastFilter>
    void addFilterToTestProvider(std::shared_ptr<MyTestProvider> testProvider, std::shared_ptr<BroadcastFilter> filter)
    {
        if (filter) {
            testProvider->addBroadcastFilter(filter);
        }
    }

    void addFilterToTestProvider(std::shared_ptr<MyTestProvider> testProvider, std::nullptr_t filter)
    {
        std::ignore = testProvider;
        std::ignore = filter;
    }

    template <typename SubscriptionListener,
              typename FireBroadcast,
              typename SubscribeTo,
              typename UnsubscribeFrom,
              typename BroadcastFilterPtr,
              typename ...T>
    void testOneShotBroadcastSubscriptionWithFiltering(SubscriptionListener subscriptionListener,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast,
                                          BroadcastFilterPtr filter,
                                          T... expectedValues) {
        std::shared_ptr<MyTestProvider> testProvider = registerProvider();
        addFilterToTestProvider(testProvider, filter);

        std::shared_ptr<tests::testProxy> testProxy = buildProxy();

        std::int64_t minInterval_ms = 50;
        std::string subscriptionId;
        auto subscriptionQos = std::make_shared<OnChangeSubscriptionQos>(
                    500000,   // validity_ms
                    1000,     // publication ttl
                    minInterval_ms);  // minInterval_ms

        subscribeTo(testProxy.get(), subscriptionListener, subscriptionQos, subscriptionId);

        (*testProvider.*fireBroadcast)(expectedValues...);

        // Wait for a subscription message to arrive
        ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(3)));
        unsubscribeFrom(testProxy.get(), subscriptionId);
    }
};

} // namespace joynr
<commit_msg>[C++] Fixed End2EndBroadcastTestBase<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2017 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

#include <boost/algorithm/string/predicate.hpp>
#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/JoynrClusterControllerRuntime.h"
#include "joynr/tests/testProxy.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MulticastSubscriptionQos.h"
#include "joynr/OnChangeSubscriptionQos.h"
#include "joynr/tests/testAbstractProvider.h"
#include "joynr/LibjoynrSettings.h"
#include "joynr/PrivateCopyAssign.h"
#include "joynr/BrokerUrl.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockLocationUpdatedSelectiveFilter.h"
#include "tests/mock/MockSubscriptionListener.h"
#include "tests/utils/MyTestProvider.h"
#include "tests/utils/PtrUtils.h"

using namespace ::testing;
using namespace joynr;

static const std::string messagingPropertiesPersistenceFileName1(
        "End2EndBroadcastTest-runtime1-joynr.persist");
static const std::string messagingPropertiesPersistenceFileName2(
        "End2EndBroadcastTest-runtime2-joynr.persist");

namespace joynr {

class End2EndBroadcastTestBase : public TestWithParam< std::tuple<std::string, std::string> > {
public:
    std::shared_ptr<JoynrClusterControllerRuntime> runtime1;
    std::shared_ptr<JoynrClusterControllerRuntime> runtime2;
    std::string baseUuid;
    std::string uuid;
    std::string domainName;
    Semaphore semaphore;
    Semaphore altSemaphore;
    joynr::tests::TestLocationUpdateSelectiveBroadcastFilterParameters filterParameters;
    std::shared_ptr<MockLocationUpdatedSelectiveFilter> filter;
    std::uint16_t registerProviderWait;
    std::uint16_t subscribeToAttributeWait;
    std::uint16_t subscribeToBroadcastWait;
    joynr::types::Localisation::GpsLocation gpsLocation;
    joynr::types::Localisation::GpsLocation gpsLocation2;
    joynr::types::Localisation::GpsLocation gpsLocation3;
    joynr::types::Localisation::GpsLocation gpsLocation4;

    End2EndBroadcastTestBase() :
        runtime1(),
        runtime2(),
        baseUuid(util::createUuid()),
        uuid( "_" + baseUuid.substr(1, baseUuid.length()-2)),
        domainName("cppEnd2EndBroadcastTest_Domain" + uuid),
        semaphore(0),
        altSemaphore(0),
        filter(std::make_shared<MockLocationUpdatedSelectiveFilter>()),
        registerProviderWait(1000),
        subscribeToAttributeWait(2000),
        subscribeToBroadcastWait(2000),
        gpsLocation(types::Localisation::GpsLocation()),
        gpsLocation2(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         2)),
        gpsLocation3(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         3)),
        gpsLocation4(types::Localisation::GpsLocation(
                         9.0,
                         51.0,
                         508.0,
                         types::Localisation::GpsFixEnum::MODE2D,
                         0.0,
                         0.0,
                         0.0,
                         0.0,
                         444,
                         444,
                         4)),
        providerParticipantId(),
        integration1Settings("test-resources/libjoynrSystemIntegration1.settings"),
        integration2Settings("test-resources/libjoynrSystemIntegration2.settings"),
        httpTransport(false)
    {
        auto settings1 = std::make_unique<Settings>(std::get<0>(GetParam()));
        auto settings2 = std::make_unique<Settings>(std::get<1>(GetParam()));
        MessagingSettings messagingSettings1(*settings1);
        MessagingSettings messagingSettings2(*settings2);
        messagingSettings1.setMessagingPropertiesPersistenceFilename(
                    messagingPropertiesPersistenceFileName1);
        messagingSettings2.setMessagingPropertiesPersistenceFilename(
                    messagingPropertiesPersistenceFileName2);

        std::string brokerProtocol = messagingSettings1.getBrokerUrl().getBrokerChannelsBaseUrl().getProtocol();
        httpTransport = boost::iequals(brokerProtocol, "http") || boost::iequals(brokerProtocol, "https");

        Settings::merge(integration1Settings, *settings1, false);

        runtime1 = std::make_shared<JoynrClusterControllerRuntime>(std::move(settings1));
        runtime1->init();

        Settings::merge(integration2Settings, *settings2, false);

        runtime2 = std::make_shared<JoynrClusterControllerRuntime>(std::move(settings2));
        runtime2->init();

        filterParameters.setCountry("Germany");
        filterParameters.setStartTime("4.00 pm");
        runtime1->start();
        runtime2->start();
    }

    ~End2EndBroadcastTestBase(){
        if (!providerParticipantId.empty()) {
            unregisterProvider();
        }
        bool deleteChannel = true;
        runtime1->stop(deleteChannel);
        runtime2->stop(deleteChannel);
        test::util::resetAndWaitUntilDestroyed(runtime1);
        test::util::resetAndWaitUntilDestroyed(runtime2);

        // Delete persisted files
        test::util::removeAllCreatedSettingsAndPersistencyFiles();
    }

private:
    std::string providerParticipantId;
    Settings integration1Settings;
    Settings integration2Settings;
    DISALLOW_COPY_AND_ASSIGN(End2EndBroadcastTestBase);
    bool httpTransport;

protected:
    bool usesHttpTransport() {
       return httpTransport;
    }

    std::shared_ptr<MyTestProvider> registerProvider() {
        return registerProvider(runtime1);
    }

    void unregisterProvider() {
        return runtime1->unregisterProvider(providerParticipantId);
    }

    std::shared_ptr<MyTestProvider> registerProvider(std::shared_ptr<JoynrClusterControllerRuntime> runtime) {
        auto testProvider = std::make_shared<MyTestProvider>();
        types::ProviderQos providerQos;
        std::chrono::milliseconds millisSinceEpoch =
                std::chrono::duration_cast<std::chrono::milliseconds>(
                        std::chrono::system_clock::now().time_since_epoch());
        providerQos.setPriority(millisSinceEpoch.count());
        providerQos.setScope(joynr::types::ProviderScope::GLOBAL);
        providerQos.setSupportsOnChangeSubscriptions(true);
        providerParticipantId = runtime->registerProvider<tests::testProvider>(domainName, testProvider, providerQos);

        // This wait is necessary, because registerProvider is async, and a lookup could occur
        // before the register has finished.
        std::this_thread::sleep_for(std::chrono::milliseconds(registerProviderWait));

        return testProvider;
    }

    std::shared_ptr<tests::testProxy> buildProxy() {
        return buildProxy(runtime2);
    }

    std::shared_ptr<tests::testProxy> buildProxy(std::shared_ptr<JoynrClusterControllerRuntime> runtime) {
        std::shared_ptr<ProxyBuilder<tests::testProxy>> testProxyBuilder
                = runtime->createProxyBuilder<tests::testProxy>(domainName);
        DiscoveryQos discoveryQos;
        discoveryQos.setArbitrationStrategy(DiscoveryQos::ArbitrationStrategy::HIGHEST_PRIORITY);
        discoveryQos.setDiscoveryTimeoutMs(30000);
        discoveryQos.setRetryIntervalMs(500);

        std::int64_t qosRoundTripTTL = 40000;

        std::shared_ptr<tests::testProxy> testProxy(testProxyBuilder
                                                   ->setMessagingQos(MessagingQos(qosRoundTripTTL))
                                                   ->setDiscoveryQos(discoveryQos)
                                                   ->build());

        return testProxy;
    }

    template <typename FireBroadcast, typename SubscribeTo, typename UnsubscribeFrom, typename T>
    void testOneShotBroadcastSubscription(const T& expectedValue,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast) {
        auto mockListener = std::make_shared<MockSubscriptionListenerOneType<T>>();

        // Use a semaphore to count and wait on calls to the mock listener
        ON_CALL(*mockListener, onReceive(Eq(expectedValue)))
                .WillByDefault(ReleaseSemaphore(&semaphore));

        testOneShotBroadcastSubscription(mockListener,
                                         subscribeTo,
                                         unsubscribeFrom,
                                         fireBroadcast,
                                         expectedValue);
    }

    template <typename SubscriptionListener,
              typename FireBroadcast,
              typename SubscribeTo,
              typename UnsubscribeFrom,
              typename ...T>
    void testOneShotBroadcastSubscription(SubscriptionListener subscriptionListener,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast,
                                          T... expectedValues) {
        std::vector<std::string> partitions({}); // TODO test with real partitions
        std::shared_ptr<MyTestProvider> testProvider = registerProvider();

        std::shared_ptr<tests::testProxy> testProxy = buildProxy();

        auto subscriptionQos = std::make_shared<MulticastSubscriptionQos>();
        subscriptionQos->setValidityMs(500000);

        std::string subscriptionId;
        subscribeTo(testProxy.get(), subscriptionListener, subscriptionQos, subscriptionId);

        (*testProvider.*fireBroadcast)(expectedValues..., partitions);

        // Wait for a subscription message to arrive
        ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(3)));
        unsubscribeFrom(testProxy.get(), subscriptionId);
    }

    template <typename BroadcastFilter>
    void addFilterToTestProvider(std::shared_ptr<MyTestProvider> testProvider, std::shared_ptr<BroadcastFilter> filter)
    {
        if (filter) {
            testProvider->addBroadcastFilter(filter);
        }
    }

    void addFilterToTestProvider(std::shared_ptr<MyTestProvider> testProvider, std::nullptr_t filter)
    {
        std::ignore = testProvider;
        std::ignore = filter;
    }

    template <typename SubscriptionListener,
              typename FireBroadcast,
              typename SubscribeTo,
              typename UnsubscribeFrom,
              typename BroadcastFilterPtr,
              typename ...T>
    void testOneShotBroadcastSubscriptionWithFiltering(SubscriptionListener subscriptionListener,
                                          SubscribeTo subscribeTo,
                                          UnsubscribeFrom unsubscribeFrom,
                                          FireBroadcast fireBroadcast,
                                          BroadcastFilterPtr filter,
                                          T... expectedValues) {
        std::shared_ptr<MyTestProvider> testProvider = registerProvider();
        addFilterToTestProvider(testProvider, filter);

        std::shared_ptr<tests::testProxy> testProxy = buildProxy();

        std::int64_t minInterval_ms = 50;
        std::string subscriptionId;
        auto subscriptionQos = std::make_shared<OnChangeSubscriptionQos>(
                    500000,   // validity_ms
                    1000,     // publication ttl
                    minInterval_ms);  // minInterval_ms

        subscribeTo(testProxy.get(), subscriptionListener, subscriptionQos, subscriptionId);

        (*testProvider.*fireBroadcast)(expectedValues...);

        // Wait for a subscription message to arrive
        ASSERT_TRUE(semaphore.waitFor(std::chrono::seconds(3)));
        unsubscribeFrom(testProxy.get(), subscriptionId);
    }
};

} // namespace joynr
<|endoftext|>


<commit_before>/*=================================================================================================
   Copyright (c) 2016 Joel de Guzman

   Licensed under a Creative Commons Attribution-ShareAlike 4.0 International.
   http://creativecommons.org/licenses/by-sa/4.0/
=================================================================================================*/
#if !defined(PHOTON_GUI_LIB_WIDGET_GALLERY_JUNE_5_2016)
#define PHOTON_GUI_LIB_WIDGET_GALLERY_JUNE_5_2016

#include <photon/support/theme.hpp>
#include <photon/support/text_utils.hpp>
#include <photon/widget.hpp>

namespace photon
{
   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Frames
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class frame : public widget
   {
   public:

      virtual void   draw(context const& ctx);
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Headings
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class heading : public widget
   {
   public:

                              heading(std::string const& text, float size_ = 1.0)
                               : _text(text)
                               , _size(size_)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      std::string             text() const                     { return _text; }
      void                    text(std::string const& text)    { _text = text; }

      using widget::text;

   private:

      std::string       _text;
      float             _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Labels
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class label : public widget
   {
   public:

                              label(std::string const& text, float size_ = 1.0)
                               : _text(text)
                               , _size(size_)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      std::string             text() const                     { return _text; }
      void                    text(std::string const& text)    { _text = text; }

      using widget::text;

   private:

      std::string       _text;
      float             _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Grid Lines
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class vgrid_lines : public widget
   {
   public:

                              vgrid_lines(float major_divisions, float minor_divisions)
                               : _major_divisions(major_divisions)
                               , _minor_divisions(minor_divisions)
                              {}

      virtual void            draw(context const& ctx);

   private:

      float                   _major_divisions;
      float                   _minor_divisions;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Groups
   ////////////////////////////////////////////////////////////////////////////////////////////////
   template <typename Heading, typename Content>
   inline auto make_group(
      Heading&&   heading,
      Content&&   content,
      bool        center_heading = true
   )
   {
      auto align_ = center_heading? 0.5 : 0;
      return
        layer(
            align_top(halign(align_, margin({ 10, 4, 10, 4 }, heading))),
            std::forward<Content>(content),
            frame{}
        );
   }

   template <typename Content>
   inline auto group(
      std::string    title,
      Content&&      content,
      float          label_size = 1.0,
      bool           center_heading = true
   )
   {
      return make_group(
         left_top_margin({ 10, 10 }, heading{ title, label_size }),
         std::forward<Content>(content), center_heading
      );
   }

   template <typename Heading, typename Content>
   inline auto make_unframed_group(
      Heading&&   heading,
      Content&&   content,
      bool        center_heading = true
   )
   {
      auto align_ = center_heading? 0.5 : 0;
      return
        layer(
            align_top(halign(align_, margin({ 10, 4, 10, 4 }, heading))),
            std::forward<Content>(content)
        );
   }

   template <typename Content>
   inline auto unframed_group(
      std::string    title,
      Content&&      content,
      float          label_size = 1.0,
      bool           center_heading = true
   )
   {
      return make_unframed_group(
         left_top_margin({ 10, 10 }, heading{ title, label_size }),
         std::forward<Content>(content), center_heading
      );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Captions
   ////////////////////////////////////////////////////////////////////////////////////////////////
   template <typename Content>
   inline auto caption(Content&& content, std::string const& title, float size = 1.0)
   {
      return
         vtile(
            std::forward<Content>(content),
            align_center(top_margin(5.0, label(title, size)))
         );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Icons
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class icon : public widget
   {
   public:
                              icon(std::uint32_t code_, float size_ = -1);

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

   private:

      std::uint32_t           _code;
      float                   _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Buttons
   ////////////////////////////////////////////////////////////////////////////////////////////////
   auto const button_margin = rect{ 10, 5, 10, 5 };
   auto const default_button_color = color{ 0, 0, 0, 0 };

   class basic_button_body : public widget
   {
   public:

      static float corner_radius;

                     basic_button_body(color body_color);
      virtual void   draw(context const& ctx);

   private:

      color          body_color;
   };

   inline basic_button_body::basic_button_body(color body_color)
    : body_color(body_color)
   {}

   template <typename Button, typename Label>
   inline Button make_button(Label&& label, color body_color = default_button_color)
   {
      auto btn_body_off = basic_button_body(body_color.level(0.9));
      auto btn_body_on = basic_button_body(body_color.opacity(0.5));

      auto btn_img_off = layer(label, btn_body_off);
      auto btn_img_on = left_top_margin({1, 1}, layer(label, btn_body_on));

      return Button(std::move(btn_img_off), std::move(btn_img_on));
   }

   template <typename Button>
   inline Button make_button(
      std::string const& text
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(label(text))
         ),
         body_color
      );
   }

   template <typename Button>
   inline Button make_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(
               htile(
                  right_margin(8, icon(icon_code)),
                  label(text)
               )
            )
         ),
         body_color
      );
   }

   template <typename Button>
   inline Button make_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(
               htile(
                  label(text),
                  left_margin(8, icon(icon_code))
               )
            )
         ),
         body_color
      );
   }

   basic_button
   button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_button
   button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_button
   button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Check Box
   ////////////////////////////////////////////////////////////////////////////////////////////////
   void draw_check_box(
      context const& ctx, std::string const& text, bool state, bool hilite
   );

   template <bool state>
   class check_box_widget : public widget
   {
   public:
                              check_box_widget(std::string const& text)
                               : _text(text)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

   private:

      std::string             _text;
   };

   template <bool state>
   widget_limits check_box_widget<state>::limits(basic_context const& ctx) const
   {
      auto& thm = get_theme();
      auto  size = measure_text(ctx.canvas, _text.c_str(), thm.label_font, thm.label_font_size);
      return { { size.x, size.y }, { size.x, size.y } };
   }

   template <bool state>
   void check_box_widget<state>::draw(context const& ctx)
   {
      draw_check_box(ctx, _text, state, ctx.bounds.includes(ctx.view.cursor_pos()));
   }

   inline basic_toggle_button check_box(std::string const& text)
   {
      return basic_toggle_button(
         check_box_widget<false>{ text }
       , check_box_widget<true>{ text }
      );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Icon Button
   ////////////////////////////////////////////////////////////////////////////////////////////////
   void draw_icon_button(context const& ctx, uint32_t code, float size, bool state, bool hilite);

   template <bool state>
   class icon_button_widget : public widget
   {
   public:
                              icon_button_widget(uint32_t code, float size)
                               : _code(code)
                               , _size(size)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      uint32_t                _code;
      float                   _size;
   };

   template <bool state>
   widget_limits icon_button_widget<state>::limits(basic_context const& ctx) const
   {
      auto  size = _size * 1.8f;
      return { { size, size }, { size, size } };
   }

   template <bool state>
   void icon_button_widget<state>::draw(context const& ctx)
   {
      draw_icon_button(ctx, _code, _size, state, ctx.bounds.includes(ctx.view.cursor_pos()));
   }

   inline basic_toggle_button icon_button(uint32_t code, float size)
   {
      return {
         icon_button_widget<false>{ code, size }
       , icon_button_widget<true>{ code, size }
      };
   }

   inline basic_toggle_button icon_button(uint32_t code1, uint32_t code2, float size)
   {
      return {
         icon_button_widget<false>{ code1, size }
       , icon_button_widget<true>{ code2, size }
      };
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Popup Button
   ////////////////////////////////////////////////////////////////////////////////////////////////
   basic_popup_button
   popup_button(
      std::string const& text
    , color body_color = default_button_color
   );

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Menu Background
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class menu_background : public widget
   {
   public:

      virtual void            draw(context const& ctx);
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Menu Items
   ////////////////////////////////////////////////////////////////////////////////////////////////
   inline auto menu_item_text(std::string const& text)
   {
      return xside_margin({ 20, 20 }, align_left(label(text)));
   }

   inline auto menu_item(std::string const& text)
   {
      return basic_menu_item(menu_item_text(text));
   }

   class menu_item_spacer_widget : public widget
   {
   public:

      virtual widget_limits limits(basic_context const& ctx) const;
      virtual void          draw(context const& ctx);
   };

   inline auto menu_item_spacer()
   {
      return menu_item_spacer_widget{};
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Text Entry
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class input_panel : public widget
   {
   public:

      virtual void draw(context const& ctx);
   };

   template <typename InputBox, typename Panel>
   inline auto input_box(
      InputBox&& text_input
    , Panel&& panel
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return layer(
         margin(
            pad,
            scroller(
               hsize(16384, std::move(text_input)),
               no_scrollbars | no_vscroll
            )
         ),
         std::move(panel)
      );
   }

   template <typename InputBox>
   inline auto input_box(
      InputBox&& text_input
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(std::move(text_input), input_panel{}, pad);
   }

   inline auto input_box(
      std::string const& placeholder
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(
         basic_input_box{ placeholder }
       , pad
      );
   }

   inline auto input_box(
      char const* placeholder
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(
         basic_input_box{ placeholder }
       , pad
      );
   }
}

#endif<commit_msg>Moved knob creation to photon gallery<commit_after>/*=================================================================================================
   Copyright (c) 2016 Joel de Guzman

   Licensed under a Creative Commons Attribution-ShareAlike 4.0 International.
   http://creativecommons.org/licenses/by-sa/4.0/
=================================================================================================*/
#if !defined(PHOTON_GUI_LIB_WIDGET_GALLERY_JUNE_5_2016)
#define PHOTON_GUI_LIB_WIDGET_GALLERY_JUNE_5_2016

#include <photon/support/theme.hpp>
#include <photon/support/text_utils.hpp>
#include <photon/widget.hpp>

namespace photon
{
   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Frames
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class frame : public widget
   {
   public:

      virtual void   draw(context const& ctx);
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Headings
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class heading : public widget
   {
   public:

                              heading(std::string const& text, float size_ = 1.0)
                               : _text(text)
                               , _size(size_)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      std::string             text() const                     { return _text; }
      void                    text(std::string const& text)    { _text = text; }

      using widget::text;

   private:

      std::string       _text;
      float             _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Labels
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class label : public widget
   {
   public:

                              label(std::string const& text, float size_ = 1.0)
                               : _text(text)
                               , _size(size_)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      std::string             text() const                     { return _text; }
      void                    text(std::string const& text)    { _text = text; }

      using widget::text;

   private:

      std::string       _text;
      float             _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Grid Lines
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class vgrid_lines : public widget
   {
   public:

                              vgrid_lines(float major_divisions, float minor_divisions)
                               : _major_divisions(major_divisions)
                               , _minor_divisions(minor_divisions)
                              {}

      virtual void            draw(context const& ctx);

   private:

      float                   _major_divisions;
      float                   _minor_divisions;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Groups
   ////////////////////////////////////////////////////////////////////////////////////////////////
   template <typename Heading, typename Content>
   inline auto make_group(
      Heading&&   heading,
      Content&&   content,
      bool        center_heading = true
   )
   {
      auto align_ = center_heading? 0.5 : 0;
      return
        layer(
            align_top(halign(align_, margin({ 10, 4, 10, 4 }, heading))),
            std::forward<Content>(content),
            frame{}
        );
   }

   template <typename Content>
   inline auto group(
      std::string    title,
      Content&&      content,
      float          label_size = 1.0,
      bool           center_heading = true
   )
   {
      return make_group(
         left_top_margin({ 10, 10 }, heading{ title, label_size }),
         std::forward<Content>(content), center_heading
      );
   }

   template <typename Heading, typename Content>
   inline auto make_unframed_group(
      Heading&&   heading,
      Content&&   content,
      bool        center_heading = true
   )
   {
      auto align_ = center_heading? 0.5 : 0;
      return
        layer(
            align_top(halign(align_, margin({ 10, 4, 10, 4 }, heading))),
            std::forward<Content>(content)
        );
   }

   template <typename Content>
   inline auto unframed_group(
      std::string    title,
      Content&&      content,
      float          label_size = 1.0,
      bool           center_heading = true
   )
   {
      return make_unframed_group(
         left_top_margin({ 10, 10 }, heading{ title, label_size }),
         std::forward<Content>(content), center_heading
      );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Captions
   ////////////////////////////////////////////////////////////////////////////////////////////////
   template <typename Content>
   inline auto caption(Content&& content, std::string const& title, float size = 1.0)
   {
      return
         vtile(
            std::forward<Content>(content),
            align_center(top_margin(5.0, label(title, size)))
         );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Icons
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class icon : public widget
   {
   public:
                              icon(std::uint32_t code_, float size_ = -1);

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

   private:

      std::uint32_t           _code;
      float                   _size;
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Buttons
   ////////////////////////////////////////////////////////////////////////////////////////////////
   auto const button_margin = rect{ 10, 5, 10, 5 };
   auto const default_button_color = color{ 0, 0, 0, 0 };

   class basic_button_body : public widget
   {
   public:

      static float corner_radius;

                     basic_button_body(color body_color);
      virtual void   draw(context const& ctx);

   private:

      color          body_color;
   };

   inline basic_button_body::basic_button_body(color body_color)
    : body_color(body_color)
   {}

   template <typename Button, typename Label>
   inline Button make_button(Label&& label, color body_color = default_button_color)
   {
      auto btn_body_off = basic_button_body(body_color.level(0.9));
      auto btn_body_on = basic_button_body(body_color.opacity(0.5));

      auto btn_img_off = layer(label, btn_body_off);
      auto btn_img_on = left_top_margin({1, 1}, layer(label, btn_body_on));

      return Button(std::move(btn_img_off), std::move(btn_img_on));
   }

   template <typename Button>
   inline Button make_button(
      std::string const& text
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(label(text))
         ),
         body_color
      );
   }

   template <typename Button>
   inline Button make_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(
               htile(
                  right_margin(8, icon(icon_code)),
                  label(text)
               )
            )
         ),
         body_color
      );
   }

   template <typename Button>
   inline Button make_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   )
   {
      return make_button<Button>(
         margin(
            button_margin,
            align_center(
               htile(
                  label(text),
                  left_margin(8, icon(icon_code))
               )
            )
         ),
         body_color
      );
   }

   basic_button
   button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_button
   button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_button
   button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_toggle_button
   toggle_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::string const& text
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::uint32_t icon_code
    , std::string const& text
    , color body_color = default_button_color
   );

   basic_latching_button
   latching_button(
      std::string const& text
    , std::uint32_t icon_code
    , color body_color = default_button_color
   );

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Check Box
   ////////////////////////////////////////////////////////////////////////////////////////////////
   void draw_check_box(
      context const& ctx, std::string const& text, bool state, bool hilite
   );

   template <bool state>
   class check_box_widget : public widget
   {
   public:
                              check_box_widget(std::string const& text)
                               : _text(text)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

   private:

      std::string             _text;
   };

   template <bool state>
   widget_limits check_box_widget<state>::limits(basic_context const& ctx) const
   {
      auto& thm = get_theme();
      auto  size = measure_text(ctx.canvas, _text.c_str(), thm.label_font, thm.label_font_size);
      return { { size.x, size.y }, { size.x, size.y } };
   }

   template <bool state>
   void check_box_widget<state>::draw(context const& ctx)
   {
      draw_check_box(ctx, _text, state, ctx.bounds.includes(ctx.view.cursor_pos()));
   }

   inline basic_toggle_button check_box(std::string const& text)
   {
      return basic_toggle_button(
         check_box_widget<false>{ text }
       , check_box_widget<true>{ text }
      );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Icon Button
   ////////////////////////////////////////////////////////////////////////////////////////////////
   void draw_icon_button(context const& ctx, uint32_t code, float size, bool state, bool hilite);

   template <bool state>
   class icon_button_widget : public widget
   {
   public:
                              icon_button_widget(uint32_t code, float size)
                               : _code(code)
                               , _size(size)
                              {}

      virtual widget_limits   limits(basic_context const& ctx) const;
      virtual void            draw(context const& ctx);

      uint32_t                _code;
      float                   _size;
   };

   template <bool state>
   widget_limits icon_button_widget<state>::limits(basic_context const& ctx) const
   {
      auto  size = _size * 1.8f;
      return { { size, size }, { size, size } };
   }

   template <bool state>
   void icon_button_widget<state>::draw(context const& ctx)
   {
      draw_icon_button(ctx, _code, _size, state, ctx.bounds.includes(ctx.view.cursor_pos()));
   }

   inline basic_toggle_button icon_button(uint32_t code, float size)
   {
      return {
         icon_button_widget<false>{ code, size }
       , icon_button_widget<true>{ code, size }
      };
   }

   inline basic_toggle_button icon_button(uint32_t code1, uint32_t code2, float size)
   {
      return {
         icon_button_widget<false>{ code1, size }
       , icon_button_widget<true>{ code2, size }
      };
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Popup Button
   ////////////////////////////////////////////////////////////////////////////////////////////////
   basic_popup_button
   popup_button(
      std::string const& text
    , color body_color = default_button_color
   );

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Menu Background
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class menu_background : public widget
   {
   public:

      virtual void            draw(context const& ctx);
   };

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Menu Items
   ////////////////////////////////////////////////////////////////////////////////////////////////
   inline auto menu_item_text(std::string const& text)
   {
      return xside_margin({ 20, 20 }, align_left(label(text)));
   }

   inline auto menu_item(std::string const& text)
   {
      return basic_menu_item(menu_item_text(text));
   }

   class menu_item_spacer_widget : public widget
   {
   public:

      virtual widget_limits limits(basic_context const& ctx) const;
      virtual void          draw(context const& ctx);
   };

   inline auto menu_item_spacer()
   {
      return menu_item_spacer_widget{};
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Text Entry
   ////////////////////////////////////////////////////////////////////////////////////////////////
   class input_panel : public widget
   {
   public:

      virtual void draw(context const& ctx);
   };

   template <typename InputBox, typename Panel>
   inline auto input_box(
      InputBox&& text_input
    , Panel&& panel
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return layer(
         margin(
            pad,
            scroller(
               hsize(16384, std::move(text_input)),
               no_scrollbars | no_vscroll
            )
         ),
         std::move(panel)
      );
   }

   template <typename InputBox>
   inline auto input_box(
      InputBox&& text_input
    , rect pad  = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(std::move(text_input), input_panel{}, pad);
   }

   inline auto input_box(
      std::string const& placeholder
    , rect pad = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(
         basic_input_box{ placeholder }
       , pad
      );
   }

   inline auto input_box(
      char const* placeholder
    , rect pad = rect{ 5, 5, 5, 4 }
   )
   {
      return input_box(
         basic_input_box{ placeholder }
       , pad
      );
   }

   ////////////////////////////////////////////////////////////////////////////////////////////////
   // Dials
   ////////////////////////////////////////////////////////////////////////////////////////////////
   inline auto dial(
      reference<dial_base>&   control
    , char const*             knob_sprite
    , float                   knob_height
    , char const*             background_image
    , char const*             caption_text
    , float                   scale = 1.0/5
    , float                   caption_size = 0.3
   )
   {
      auto knob =  sprite{ knob_sprite, knob_height * scale, scale };
      auto lines = image{ background_image, scale };
      control = reference<dial_base>(share(dial(knob)));

      return
         align_center_middle(
            caption(
               layer(
                  align_center_middle(control),
                  lines
               ),
               caption_text,  // caption
               0.3            // relative caption text size
            )
         );
   }
}

#endif<|endoftext|>


<commit_before>// This source code is intended for use in the teaching course "Vision-Based Navigation" in summer term 2015 at Technical University Munich only.
// Copyright 2015 Vladyslav Usenko, Joerg Stueckler, Technical University Munich

#ifndef UAV_CONTROLLER_H_
#define UAV_CONTROLLER_H_

#include <ros/ros.h>
#include <std_srvs/Empty.h>

#include <sensor_msgs/Imu.h>
#include <geometry_msgs/PoseWithCovarianceStamped.h>

#include <sophus/se3.hpp>
#include <eigen_conversions/eigen_msg.h>

#include <mav_msgs/CommandAttitudeThrust.h>
#include <mav_msgs/CommandRateThrust.h>
#include <mav_msgs/CommandMotorSpeed.h>

#include <boost/thread/mutex.hpp>

#include <se3ukf.hpp>

#include <list>
#include <fstream>

template<typename _Scalar>
class UAVController {

private:

	typedef Sophus::SE3Group<_Scalar> SE3Type;
	typedef Sophus::SO3Group<_Scalar> SO3Type;
	typedef Eigen::Quaternion<_Scalar> Quaternion;

	typedef Eigen::Matrix<_Scalar, 3, 1> Vector3;
	typedef Eigen::Matrix<_Scalar, 6, 1> Vector6;
	typedef Eigen::Matrix<_Scalar, 12, 1> Vector12;
	typedef Eigen::Matrix<_Scalar, 15, 1> Vector15;

	typedef Eigen::Matrix<_Scalar, 3, 3> Matrix3;
	typedef Eigen::Matrix<_Scalar, 6, 6> Matrix6;
	typedef Eigen::Matrix<_Scalar, 12, 12> Matrix12;
	typedef Eigen::Matrix<_Scalar, 15, 15> Matrix15;

	ros::Publisher command_pub;
	ros::Subscriber imu_sub;
	ros::Subscriber pose_sub;
	ros::Subscriber ground_truth_sub;

	// Switch between ground truth and ukf for controller
	bool use_ground_thruth_data;

	// Ground thruth data
	SE3Type ground_truth_pose;
	Vector3 ground_truth_linear_velocity;
	double ground_truth_time;

	// Constants
	_Scalar g;
	_Scalar m;
	SE3Type T_imu_cam;
	SE3Type  initial_pose;
	Matrix15 initial_state_covariance;
	Matrix3 gyro_noise;
	Matrix3 accel_noise;
	Matrix6 measurement6d_noise;

	// Pose to hoover at
	SE3Type desired_pose;

	mav_msgs::CommandAttitudeThrust command;

	void imuCallback(const sensor_msgs::ImuConstPtr& msg) {
		Eigen::Vector3d accel_measurement, gyro_measurement;
		tf::vectorMsgToEigen(msg->angular_velocity, gyro_measurement);
		tf::vectorMsgToEigen(msg->linear_acceleration, accel_measurement);

        sendControlSignal();
	}

	void groundTruthPoseCallback(
			const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) {
		Eigen::Quaterniond orientation;
		Eigen::Vector3d position;

		tf::pointMsgToEigen(msg->pose.pose.position, position);
		tf::quaternionMsgToEigen(msg->pose.pose.orientation, orientation);

		SE3Type pose(orientation.cast<_Scalar>(), position.cast<_Scalar>());

		ground_truth_linear_velocity = (pose.translation()
				- ground_truth_pose.translation())
				/ (msg->header.stamp.toSec() - ground_truth_time);

		ground_truth_pose = pose;
		ground_truth_time = msg->header.stamp.toSec();
	}

	void pose1Callback(
			const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) {
		Eigen::Quaterniond orientation;
		Eigen::Vector3d position;

		tf::pointMsgToEigen(msg->pose.pose.position, position);
		tf::quaternionMsgToEigen(msg->pose.pose.orientation, orientation);

		SE3Type pose(orientation, position);
	}

    // exercise 1 d)
	mav_msgs::CommandAttitudeThrust computeCommandFromForce(
			const Vector3 & control_force, const SE3Type & pose,
			double delta_time) {
        // TODO: implement
        float yaw = 0.5f; // ?
        // f = m * a    ==>     a = f / m
        Vector3 a = control_force/m;
        float roll =  1.0f/g * (a.x()*sin(yaw) - a.y()*cos(yaw));
        float pitch = 1.0f/g * (a.x()*cos(yaw) + a.y()*sin(yaw));
        float thrust = a.z() + m*g;

        // mav_msgs::CommandAttitudeThrust_(roll, pitch, yaw, thrust)
        mav_msgs::CommandAttitudeThrust msg;
        msg.roll = roll;    // [rad]
        msg.pitch = pitch;  // [rad]
        msg.yaw_rate = yaw; // [rad/s]
        msg.thrust = thrust;
        return msg;
	}

    // exercise 1 c)
	Vector3 computeDesiredForce(const SE3Type & pose, const Vector3 & linear_velocity,
			double delta_time) {
        // TODO: implement
        SE3Type curr_pose;
        Vector3 curr_velocity;
        getPoseAndVelocity(curr_pose, curr_velocity);

        float kp = 1.0f; // proportional gains of the PID controller
        float kd = 1.0f; // differential gains of the PID controller
        float ki = 1.0f; // integral gains of the PID controller

        // x'' = kp*(xd-x) + kd*(xd'-x') + ki*integral(xd-x, delta_time)
        Vector3 a = kp*(pose.translation() - curr_pose.translation()) +
                    kd*(linear_velocity - curr_velocity) +
                    ki*delta_time*(pose.translation() - curr_pose.translation());
        return m*a; // f = m * a
	}

	void getPoseAndVelocity(SE3Type & pose, Vector3 & linear_velocity) {
		if (use_ground_thruth_data) {
			pose = ground_truth_pose;
			linear_velocity = ground_truth_linear_velocity;
		} else {

		}
	}

public:

	typedef boost::shared_ptr<UAVController> Ptr;

	UAVController(ros::NodeHandle & nh) :
		 ground_truth_time(0) {

		use_ground_thruth_data = false;

		// ========= Constants ===================================//
        g = 9.8; // in rate_controller g = 9.81
        m = 1.55; // in rate_controller m = 1.56779
		initial_state_covariance = Matrix15::Identity() * 0.001;
		gyro_noise = Matrix3::Identity() * 0.0033937;
		accel_noise = Matrix3::Identity() * 0.04;
		measurement6d_noise = Matrix6::Identity() * 0.01;
		initial_pose.translation() << 0, 0, 0.08;

		// Set simulated camera to IMU transformation
		Eigen::AngleAxisd rollAngle(0.2, Eigen::Vector3d::UnitX());
		Eigen::AngleAxisd pitchAngle(-0.1, Eigen::Vector3d::UnitY());
		Eigen::AngleAxisd yawAngle(0.3, Eigen::Vector3d::UnitZ());

		Eigen::Quaterniond q = yawAngle * pitchAngle * rollAngle;
		T_imu_cam.setQuaternion(q);
		T_imu_cam.translation() << 0.03, -0.07, 0.1;

        // set desired position
        setDesiredPose(SE3Type(SO3Type::exp(Vector3(0,0,0)),Vector3(0,0,1)));

		// Init subscribers and publishers
		imu_sub = nh.subscribe("imu", 10, &UAVController<_Scalar>::imuCallback,
				this);
		pose_sub = nh.subscribe("pose1", 10,
				&UAVController<_Scalar>::pose1Callback, this);
		ground_truth_sub = nh.subscribe("ground_truth/pose", 10,
				&UAVController<_Scalar>::groundTruthPoseCallback, this);

		command_pub = nh.advertise<mav_msgs::CommandAttitudeThrust>(
				"command/attitude", 10);

		// Wake up simulation, from this point on, you have 30s to initialize
		// everything and fly to the evaluation position (x=0m y=0m z=1m).
		ROS_INFO("Waking up simulation ... ");
		std_srvs::Empty srv;
		bool ret = ros::service::call("/gazebo/unpause_physics", srv);

		if (ret)
			ROS_INFO("... ok");
		else {
			ROS_FATAL("could not wake up gazebo");
			exit(-1);
		}
	}

	~UAVController() {
	}

    // exercise 1 e)
	void sendControlSignal() {
        // TODO: implement
        SE3Type curr_pose;
        Vector3 curr_velocity;
        Vector3 desired_velocity = Vector3(0,0,0);

        double delta_time = 0.001d; // ?

        getPoseAndVelocity(curr_pose, curr_velocity);
        Vector3 dforce = computeDesiredForce(desired_pose, desired_velocity, delta_time);

        command_pub.publish( computeCommandFromForce(dforce, desired_pose /*?*/, delta_time) );
    }

	void setDesiredPose(const SE3Type & p) {
		desired_pose = p;
	}

	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

#endif /* UAV_CONTROLLER_H_ */
<commit_msg>Exercise 3 part 1 finsihed. May require further tuning for kp, kd, ki and testing.<commit_after>// This source code is intended for use in the teaching course "Vision-Based Navigation" in summer term 2015 at Technical University Munich only.
// Copyright 2015 Vladyslav Usenko, Joerg Stueckler, Technical University Munich

#ifndef UAV_CONTROLLER_H_
#define UAV_CONTROLLER_H_

#include <ros/ros.h>
#include <std_srvs/Empty.h>

#include <sensor_msgs/Imu.h>
#include <geometry_msgs/PoseWithCovarianceStamped.h>

#include <sophus/se3.hpp>
#include <eigen_conversions/eigen_msg.h>

#include <mav_msgs/CommandAttitudeThrust.h>
#include <mav_msgs/CommandRateThrust.h>
#include <mav_msgs/CommandMotorSpeed.h>

#include <boost/thread/mutex.hpp>

#include <se3ukf.hpp>

#include <list>
#include <fstream>

template<typename _Scalar>
class UAVController {

private:

	typedef Sophus::SE3Group<_Scalar> SE3Type;
	typedef Sophus::SO3Group<_Scalar> SO3Type;
	typedef Eigen::Quaternion<_Scalar> Quaternion;

	typedef Eigen::Matrix<_Scalar, 3, 1> Vector3;
	typedef Eigen::Matrix<_Scalar, 6, 1> Vector6;
	typedef Eigen::Matrix<_Scalar, 12, 1> Vector12;
	typedef Eigen::Matrix<_Scalar, 15, 1> Vector15;

	typedef Eigen::Matrix<_Scalar, 3, 3> Matrix3;
	typedef Eigen::Matrix<_Scalar, 6, 6> Matrix6;
	typedef Eigen::Matrix<_Scalar, 12, 12> Matrix12;
	typedef Eigen::Matrix<_Scalar, 15, 15> Matrix15;

	ros::Publisher command_pub;
	ros::Subscriber imu_sub;
	ros::Subscriber pose_sub;
	ros::Subscriber ground_truth_sub;

	// Switch between ground truth and ukf for controller
	bool use_ground_thruth_data;

	// Ground thruth data
	SE3Type ground_truth_pose;
	Vector3 ground_truth_linear_velocity;
	double ground_truth_time;

	// Constants
	_Scalar g;
	_Scalar m;
	SE3Type T_imu_cam;
	SE3Type  initial_pose;
	Matrix15 initial_state_covariance;
	Matrix3 gyro_noise;
	Matrix3 accel_noise;
	Matrix6 measurement6d_noise;

	// Pose to hoover at
	SE3Type desired_pose;

	mav_msgs::CommandAttitudeThrust command;

    // debug
    Vector3 position_integral;

	void imuCallback(const sensor_msgs::ImuConstPtr& msg) {
		Eigen::Vector3d accel_measurement, gyro_measurement;
		tf::vectorMsgToEigen(msg->angular_velocity, gyro_measurement);
		tf::vectorMsgToEigen(msg->linear_acceleration, accel_measurement);

        sendControlSignal();
	}

	void groundTruthPoseCallback(
			const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) {
		Eigen::Quaterniond orientation;
		Eigen::Vector3d position;

		tf::pointMsgToEigen(msg->pose.pose.position, position);
		tf::quaternionMsgToEigen(msg->pose.pose.orientation, orientation);

		SE3Type pose(orientation.cast<_Scalar>(), position.cast<_Scalar>());

		ground_truth_linear_velocity = (pose.translation()
				- ground_truth_pose.translation())
				/ (msg->header.stamp.toSec() - ground_truth_time);

		ground_truth_pose = pose;
		ground_truth_time = msg->header.stamp.toSec();
	}

	void pose1Callback(
			const geometry_msgs::PoseWithCovarianceStampedConstPtr& msg) {
		Eigen::Quaterniond orientation;
		Eigen::Vector3d position;

		tf::pointMsgToEigen(msg->pose.pose.position, position);
		tf::quaternionMsgToEigen(msg->pose.pose.orientation, orientation);

		SE3Type pose(orientation, position);
	}

    // TODO: exercise 1 d)
	mav_msgs::CommandAttitudeThrust computeCommandFromForce(
			const Vector3 & control_force, const SE3Type & pose,
			double delta_time) {
        float yaw = 0.f;
        // f = m * a    ==>     a = f / m
        Vector3 a = control_force/m;
        float roll =  1.0f/g * (a.x()*sin(yaw) - a.y()*cos(yaw));
        float pitch = 1.0f/g * (a.x()*cos(yaw) + a.y()*sin(yaw));
        float thrust = a.z() + m*g;

        mav_msgs::CommandAttitudeThrust msg;
        msg.roll = roll;    // [rad]
        msg.pitch = pitch;  // [rad]
        msg.yaw_rate = yaw; // [rad/s]
        msg.thrust = thrust;
        return msg;
	}

    // TODO: exercise 1 c)
	Vector3 computeDesiredForce(const SE3Type & pose, const Vector3 & linear_velocity,
			double delta_time) {
        SE3Type curr_pose;
        Vector3 curr_velocity;
        getPoseAndVelocity(curr_pose, curr_velocity);

        const float kp = 4.0f; // proportional gains of the PID controller
        const float kd = 4.0f; // differential gains of the PID controller
        const float ki = 8.0f; // integral gains of the PID controller

        // x'' = kp*(xd-x) + kd*(xd'-x') + ki*integral(xd-x, delta_time)
        position_integral += delta_time*(pose.translation() - curr_pose.translation());
        Vector3 a = kp*(pose.translation() - curr_pose.translation()) +
                    kd*(linear_velocity - curr_velocity) +
                    ki*position_integral;
        return m*a; // f = m * a
	}

    // TODO: exercise 1 b)
	void getPoseAndVelocity(SE3Type & pose, Vector3 & linear_velocity) {
		if (use_ground_thruth_data) {
			pose = ground_truth_pose;
			linear_velocity = ground_truth_linear_velocity;
		} else {

		}
	}

public:

	typedef boost::shared_ptr<UAVController> Ptr;

	UAVController(ros::NodeHandle & nh) :
         ground_truth_time(0),
         position_integral(0,0,0){

        use_ground_thruth_data = true;

		// ========= Constants ===================================//
        g = 9.8; // in rate_controller g = 9.81
        m = 1.55; // in rate_controller m = 1.56779
		initial_state_covariance = Matrix15::Identity() * 0.001;
		gyro_noise = Matrix3::Identity() * 0.0033937;
		accel_noise = Matrix3::Identity() * 0.04;
		measurement6d_noise = Matrix6::Identity() * 0.01;
		initial_pose.translation() << 0, 0, 0.08;

		// Set simulated camera to IMU transformation
		Eigen::AngleAxisd rollAngle(0.2, Eigen::Vector3d::UnitX());
		Eigen::AngleAxisd pitchAngle(-0.1, Eigen::Vector3d::UnitY());
		Eigen::AngleAxisd yawAngle(0.3, Eigen::Vector3d::UnitZ());

		Eigen::Quaterniond q = yawAngle * pitchAngle * rollAngle;
		T_imu_cam.setQuaternion(q);
		T_imu_cam.translation() << 0.03, -0.07, 0.1;

		// Init subscribers and publishers
		imu_sub = nh.subscribe("imu", 10, &UAVController<_Scalar>::imuCallback,
				this);
		pose_sub = nh.subscribe("pose1", 10,
				&UAVController<_Scalar>::pose1Callback, this);
		ground_truth_sub = nh.subscribe("ground_truth/pose", 10,
				&UAVController<_Scalar>::groundTruthPoseCallback, this);

		command_pub = nh.advertise<mav_msgs::CommandAttitudeThrust>(
				"command/attitude", 10);

		// Wake up simulation, from this point on, you have 30s to initialize
		// everything and fly to the evaluation position (x=0m y=0m z=1m).
		ROS_INFO("Waking up simulation ... ");
		std_srvs::Empty srv;
		bool ret = ros::service::call("/gazebo/unpause_physics", srv);

		if (ret)
			ROS_INFO("... ok");
		else {
			ROS_FATAL("could not wake up gazebo");
			exit(-1);
		}
	}

	~UAVController() {
	}

    // TODO: exercise 1 e)
	void sendControlSignal() {
        SE3Type curr_pose;
        Vector3 curr_velocity;
        Vector3 desired_velocity = Vector3(0,0,0);

        double delta_time = 0.001d; // ?

        getPoseAndVelocity(curr_pose, curr_velocity);
        Vector3 dforce = computeDesiredForce(desired_pose, desired_velocity, delta_time);

        command = computeCommandFromForce(dforce, desired_pose, delta_time);
        command_pub.publish(command);
    }

	void setDesiredPose(const SE3Type & p) {
		desired_pose = p;
	}

	EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};

#endif /* UAV_CONTROLLER_H_ */
<|endoftext|>


<commit_before>// This file is a part of Simdee, see homepage at http://github.com/tufak/simdee
// This file is distributed under the MIT license.

#ifndef SIMDEE_UTIL_MALLOC_HPP
#define SIMDEE_UTIL_MALLOC_HPP

#include <type_traits>
#include <exception>
#include <cstdlib>
#include <cstdint>
#include "noexcept.hpp"

namespace sd {

    namespace detail {
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
        // type_traits is buggered in libstdc++ up until GCC 5
        template <typename T>
        using is_trivially_default_constructible = std::has_trivial_default_constructor<T>;
#else
        template <typename T>
        using is_trivially_default_constructible = std::is_trivially_default_constructible<T>;
#endif

        inline constexpr bool is_pow2(std::size_t x) {
            return x && (x & (x - 1)) == 0;
        }

        template <typename T, std::size_t Align>
        struct aligned_allocator;
        template <typename T, std::size_t Align = alignof(T), bool Switch = (Align > alignof(double))>
        struct alloc;

        template <typename T, std::size_t Align>
        struct alloc<T, Align, false> {
            static T* malloc(std::size_t bytes) {
                return (T*)std::malloc(bytes);
            }
            static void free(T* ptr) {
                std::free(ptr);
            }
            using allocator = std::allocator<T>;
            using deleter = std::default_delete<T>;
        };

        template <typename T, std::size_t Align>
        struct alloc<T, Align, true> {
            static_assert(detail::is_pow2(Align), "alignment must be a power of 2");
            static_assert(Align <= 128, "alignment is too large");
            static_assert(Align > alignof(double), "alignment is too small -- use malloc");

            static T* malloc(std::size_t bytes) {
                auto orig = (uintptr_t)std::malloc(bytes + Align);
                if (orig == 0) return nullptr;
                auto aligned = (orig + Align) & ~(Align - 1);
                auto offset = int8_t(orig - aligned);
                ((int8_t*)aligned)[-1] = offset;
                return (T*)aligned;
            }

            static void free(T* aligned) {
                if (aligned == nullptr) return;
                auto offset = ((int8_t*)aligned)[-1];
                auto orig = uintptr_t(aligned) + offset;
                std::free((void*)orig);
            }

            using allocator = aligned_allocator<T, Align>;

            struct deleter {
                template <typename S>
                void operator()(S* ptr) {
                    free(ptr);
                }
            };
        };

        template <typename T, std::size_t Align>
        struct aligned_allocator {
            using value_type = T;
            using alloc_t = alloc<T, Align>;

            aligned_allocator() = default;

            template <typename S>
            aligned_allocator(const aligned_allocator<S, Align>&) {}

            T* allocate(std::size_t count) const SIMDEE_NOEXCEPT {
                return alloc_t::malloc(sizeof(T) * count);
            }

            void deallocate(T* ptr, std::size_t) const SIMDEE_NOEXCEPT {
                alloc_t::free(ptr);
            }

            void destroy(T* ptr) const SIMDEE_NOEXCEPT_IF(std::is_nothrow_destructible<T>::value) {
                if (!std::is_trivially_destructible<T>::value) {
                    ptr->~T();
                }
                else {
                    no_op(ptr); // just to suppress MSVC warning "ptr not referenced"
                }
            }

            static void no_op(T*) {}

            void construct(T* ptr) const SIMDEE_NOEXCEPT_IF(std::is_nothrow_constructible<T>::value) {
                if (!is_trivially_default_constructible<T>::value) {
                    new (ptr)T;
                }
            }

            template <typename A1, typename... A>
            void construct(T* ptr, A1&& a1, A&&... a2) const {
                new (ptr)T(std::forward<A1>(a1), std::forward<A...>(a2)...);
            }

            // default rebind should do just this, doesn't seem to work in MSVC though
            template <typename S>
            struct rebind {
                using other = aligned_allocator<S, Align>;
            };
        };

        template <typename T, typename U, std::size_t TS, std::size_t US>
        bool operator==(const aligned_allocator<T, TS>&, const aligned_allocator<U, US>&) { return true; }
        template <typename T, typename U, std::size_t TS, std::size_t US>
        bool operator!=(const aligned_allocator<T, TS>&, const aligned_allocator<U, US>&) { return false; }
    }
}

#endif // SIMDEE_UTIL_MALLOC_HPP
<commit_msg>Make allocator's operator== inline<commit_after>// This file is a part of Simdee, see homepage at http://github.com/tufak/simdee
// This file is distributed under the MIT license.

#ifndef SIMDEE_UTIL_MALLOC_HPP
#define SIMDEE_UTIL_MALLOC_HPP

#include <type_traits>
#include <exception>
#include <cstdlib>
#include <cstdint>
#include "noexcept.hpp"

namespace sd {

    namespace detail {
#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803)
        // type_traits is buggered in libstdc++ up until GCC 5
        template <typename T>
        using is_trivially_default_constructible = std::has_trivial_default_constructor<T>;
#else
        template <typename T>
        using is_trivially_default_constructible = std::is_trivially_default_constructible<T>;
#endif

        inline constexpr bool is_pow2(std::size_t x) {
            return x && (x & (x - 1)) == 0;
        }

        template <typename T, std::size_t Align>
        struct aligned_allocator;
        template <typename T, std::size_t Align = alignof(T), bool Switch = (Align > alignof(double))>
        struct alloc;

        template <typename T, std::size_t Align>
        struct alloc<T, Align, false> {
            static T* malloc(std::size_t bytes) {
                return (T*)std::malloc(bytes);
            }
            static void free(T* ptr) {
                std::free(ptr);
            }
            using allocator = std::allocator<T>;
            using deleter = std::default_delete<T>;
        };

        template <typename T, std::size_t Align>
        struct alloc<T, Align, true> {
            static_assert(detail::is_pow2(Align), "alignment must be a power of 2");
            static_assert(Align <= 128, "alignment is too large");
            static_assert(Align > alignof(double), "alignment is too small -- use malloc");

            static T* malloc(std::size_t bytes) {
                auto orig = (uintptr_t)std::malloc(bytes + Align);
                if (orig == 0) return nullptr;
                auto aligned = (orig + Align) & ~(Align - 1);
                auto offset = int8_t(orig - aligned);
                ((int8_t*)aligned)[-1] = offset;
                return (T*)aligned;
            }

            static void free(T* aligned) {
                if (aligned == nullptr) return;
                auto offset = ((int8_t*)aligned)[-1];
                auto orig = uintptr_t(aligned) + offset;
                std::free((void*)orig);
            }

            using allocator = aligned_allocator<T, Align>;

            struct deleter {
                template <typename S>
                void operator()(S* ptr) {
                    free(ptr);
                }
            };
        };

        template <typename T, std::size_t Align>
        struct aligned_allocator {
            using value_type = T;
            using alloc_t = alloc<T, Align>;

            aligned_allocator() = default;

            template <typename S>
            aligned_allocator(const aligned_allocator<S, Align>&) {}

            T* allocate(std::size_t count) const SIMDEE_NOEXCEPT {
                return alloc_t::malloc(sizeof(T) * count);
            }

            void deallocate(T* ptr, std::size_t) const SIMDEE_NOEXCEPT {
                alloc_t::free(ptr);
            }

            void destroy(T* ptr) const SIMDEE_NOEXCEPT_IF(std::is_nothrow_destructible<T>::value) {
                if (!std::is_trivially_destructible<T>::value) {
                    ptr->~T();
                }
                else {
                    no_op(ptr); // just to suppress MSVC warning "ptr not referenced"
                }
            }

            static void no_op(T*) {}

            void construct(T* ptr) const SIMDEE_NOEXCEPT_IF(std::is_nothrow_constructible<T>::value) {
                if (!is_trivially_default_constructible<T>::value) {
                    new (ptr)T;
                }
            }

            template <typename A1, typename... A>
            void construct(T* ptr, A1&& a1, A&&... a2) const {
                new (ptr)T(std::forward<A1>(a1), std::forward<A...>(a2)...);
            }

            // default rebind should do just this, doesn't seem to work in MSVC though
            template <typename S>
            struct rebind {
                using other = aligned_allocator<S, Align>;
            };
        };

        template <typename T, typename U, std::size_t TS, std::size_t US>
        inline bool operator==(const aligned_allocator<T, TS>&, const aligned_allocator<U, US>&) { return true; }
        template <typename T, typename U, std::size_t TS, std::size_t US>
        inline bool operator!=(const aligned_allocator<T, TS>&, const aligned_allocator<U, US>&) { return false; }
    }
}

#endif // SIMDEE_UTIL_MALLOC_HPP
<|endoftext|>