<commit_before>/*
 * (C) Copyright 2014 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "WebSocketRegistrar.hpp"
#include <json/json.h>
#include <gst/gst.h>

#define GST_CAT_DEFAULT kurento_websocket_registrar
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoWebSocketRegistrar"

namespace kurento
{

const std::chrono::milliseconds DEFAULT_WAIT_TIME (100);
const std::chrono::seconds MAX_WAIT_TIME (10);

typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;

WebSocketRegistrar::WebSocketRegistrar (const std::string &registrarAddress,
                                        const std::string &localAddress,
                                        ushort localPort,
                                        ushort localSecurePort,
                                        const std::string &path) :
  localAddress (localAddress), localPort (localPort),
  localSecurePort (localSecurePort), path (path),
  registrarAddress (registrarAddress)
{
  GST_INFO ("Registrar will be performed to: %s", registrarAddress.c_str () );
}

WebSocketRegistrar::~WebSocketRegistrar ()
{
  if (!finished) {
    stop();
  }
}

void
WebSocketRegistrar::start()
{
  if (registrarAddress.empty() || localAddress.empty () ) {
    return;
  }

  waitTime = DEFAULT_WAIT_TIME;
  finished = false;

  thread = std::thread (std::bind (&WebSocketRegistrar::connectRegistrar, this) );
}

void
WebSocketRegistrar::stop()
{
  if (registrarAddress.empty() || localAddress.empty () ) {
    return;
  }

  finished = true;

  try {
    if (secure) {
      secureClient->close (connection, websocketpp::close::status::going_away,
                           "terminating");
    } else {
      client->close (connection, websocketpp::close::status::going_away,
                     "terminating");
    }
  } catch (...) {

  }

  std::unique_lock<std::mutex> lock (mutex);
  cond.notify_all ();
  lock.unlock ();

  if (thread.get_id () != std::this_thread::get_id () ) {
    thread.join();
  }
}

void
WebSocketRegistrar::connectRegistrar ()
{
  websocketpp::lib::error_code ec;

  if (registrarAddress.empty () ) {
    return;
  }

  while (!finished) {
    boost::asio::io_service ios;

    GST_INFO ("Connecting registrar");

    if (registrarAddress.size() >= 3 && registrarAddress.substr (0, 3) == "wss") {
      secureClient = std::shared_ptr<SecureWebSocketClient> (new
                     SecureWebSocketClient() );
      secureClient->clear_access_channels (websocketpp::log::alevel::all);
      secureClient->clear_error_channels (websocketpp::log::elevel::all);

      // Register our handlers
      secureClient->set_open_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                        std::shared_ptr<SecureWebSocketClient>,
                                        websocketpp::connection_hdl) ) &WebSocketRegistrar::connectionOpen, this,
                                      secureClient, std::placeholders::_1) );
      secureClient->set_message_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                           std::shared_ptr<SecureWebSocketClient>, websocketpp::connection_hdl,
                                           SecureWebSocketClient::message_ptr) ) &WebSocketRegistrar::receivedMessage,
                                         this, secureClient, std::placeholders::_1, std::placeholders::_2) );
      secureClient->set_close_handler (std::bind (&WebSocketRegistrar::closedHandler,
                                       this, std::placeholders::_1) );
      secureClient->set_tls_init_handler ( [] (websocketpp::connection_hdl hdl) ->
      context_ptr {
        context_ptr ctx (new boost::asio::ssl::context (boost::asio::ssl::context::tlsv1) );

        try {
          ctx->set_options (boost::asio::ssl::context::default_workarounds |
          boost::asio::ssl::context::no_sslv2 |
          boost::asio::ssl::context::single_dh_use);
        } catch (std::exception &e)
        {
          GST_ERROR ("Error setting tls on registrar connection: %s", e.what() );
        }

        return ctx;
      });

      // Initialize ASIO
      secureClient->init_asio (&ios);

      SecureWebSocketClient::connection_ptr con = secureClient->get_connection (
            registrarAddress, ec);

      if (con) {
        secure = true;
        secureClient->connect (con);
        secureClient->run();
      } else {
        GST_ERROR ("Cannot create connection %s", ec.message().c_str() );
      }
    } else {
      client = std::shared_ptr<WebSocketClient> (new WebSocketClient() );
      client->clear_access_channels (websocketpp::log::alevel::all);
      client->clear_error_channels (websocketpp::log::elevel::all);

      // Register our handlers
      client->set_open_handler (std::bind ( (void (WebSocketRegistrar::*) (
          std::shared_ptr<WebSocketClient>,
          websocketpp::connection_hdl) ) &WebSocketRegistrar::connectionOpen, this,
                                            client, std::placeholders::_1) );
      client->set_message_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                     std::shared_ptr<WebSocketClient>, websocketpp::connection_hdl,
                                     WebSocketClient::message_ptr) ) &WebSocketRegistrar::receivedMessage, this,
                                   client, std::placeholders::_1, std::placeholders::_2) );
      client->set_close_handler (std::bind (&WebSocketRegistrar::closedHandler, this,
                                            std::placeholders::_1) );

      // Initialize ASIO
      client->init_asio (&ios);

      WebSocketClient::connection_ptr con = client->get_connection (registrarAddress,
                                            ec);

      if (con) {
        secure = false;
        client->connect (con);
        client->run();
      } else {
        GST_ERROR ("Cannot create connection %s", ec.message().c_str() );
      }
    }

    if (finished) {
      break;
    }

    GST_INFO ("Registrar disconnected, trying to reconnect in %" G_GINT64_FORMAT
              " ms", waitTime.count () );

    std::unique_lock<std::mutex> lock (mutex);
    cond.wait_for (lock, waitTime);
    lock.unlock ();

    if (waitTime < (MAX_WAIT_TIME / 2) ) {
      waitTime = waitTime * 2;
    } else {
      waitTime = MAX_WAIT_TIME;
    }
  }

  GST_INFO ("Terminating");
}

template <typename ClientType>
void
WebSocketRegistrar::connectionOpen (std::shared_ptr<ClientType> client,
                                    websocketpp::connection_hdl hdl)
{
  Json::Value req;
  Json::Value params;
  Json::FastWriter writer;
  std::string request;

  waitTime = DEFAULT_WAIT_TIME;
  connection = hdl;

  req["jsonrpc"] = "2.0";
  req["method"] = "register";

  if (localSecurePort > 0) {
    params["ws"] = "wss://" + localAddress + ":" + std::to_string (
                     localSecurePort) + "/" + path;
  } else {
    params["ws"] = "ws://" + localAddress + ":" + std::to_string (
                     localPort) + "/" + path;
  }

  req["params"] = params;

  request = writer.write (req);
  GST_DEBUG ("Registrar open, sending message: %s", request.c_str() );

  try {
    client->send (hdl, request, websocketpp::frame::opcode::TEXT);
  } catch (websocketpp::exception e) {
    GST_ERROR ("Cannot send message to remote");
  }
}

void
WebSocketRegistrar::closedHandler (websocketpp::connection_hdl hdl)
{
  GST_DEBUG ("Registrar closed");
}

template <typename ClientType>
void
WebSocketRegistrar::receivedMessage (std::shared_ptr<ClientType> client,
                                     websocketpp::connection_hdl hdl,
                                     typename ClientType::message_ptr msg)
{
  GST_DEBUG ("Message: %s", msg->get_payload().c_str() );
}

WebSocketRegistrar::StaticConstructor WebSocketRegistrar::staticConstructor;

WebSocketRegistrar::StaticConstructor::StaticConstructor()
{
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}

} /* kurento */
<commit_msg>WebSocketRegistrar: Speed up stopping<commit_after>/*
 * (C) Copyright 2014 Kurento (http://kurento.org/)
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl-2.1.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 */

#include "WebSocketRegistrar.hpp"
#include <json/json.h>
#include <gst/gst.h>

#define GST_CAT_DEFAULT kurento_websocket_registrar
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
#define GST_DEFAULT_NAME "KurentoWebSocketRegistrar"

namespace kurento
{

const std::chrono::milliseconds DEFAULT_WAIT_TIME (100);
const std::chrono::seconds MAX_WAIT_TIME (10);

typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;

WebSocketRegistrar::WebSocketRegistrar (const std::string &registrarAddress,
                                        const std::string &localAddress,
                                        ushort localPort,
                                        ushort localSecurePort,
                                        const std::string &path) :
  localAddress (localAddress), localPort (localPort),
  localSecurePort (localSecurePort), path (path),
  registrarAddress (registrarAddress)
{
  GST_INFO ("Registrar will be performed to: %s", registrarAddress.c_str () );
}

WebSocketRegistrar::~WebSocketRegistrar ()
{
  if (!finished) {
    stop();
  }
}

void
WebSocketRegistrar::start()
{
  if (registrarAddress.empty() || localAddress.empty () ) {
    return;
  }

  waitTime = DEFAULT_WAIT_TIME;
  finished = false;

  thread = std::thread (std::bind (&WebSocketRegistrar::connectRegistrar, this) );
}

void
WebSocketRegistrar::stop()
{
  if (registrarAddress.empty() || localAddress.empty () ) {
    return;
  }

  finished = true;

  try {
    if (secure) {
      secureClient->close (connection, websocketpp::close::status::going_away,
                           "terminating");
      secureClient->get_io_service().stop();
    } else {
      client->close (connection, websocketpp::close::status::going_away,
                     "terminating");
      client->get_io_service().stop();
    }
  } catch (...) {

  }

  std::unique_lock<std::mutex> lock (mutex);
  cond.notify_all ();
  lock.unlock ();

  if (thread.get_id () != std::this_thread::get_id () ) {
    thread.join();
  }
}

void
WebSocketRegistrar::connectRegistrar ()
{
  websocketpp::lib::error_code ec;

  if (registrarAddress.empty () ) {
    return;
  }

  while (!finished) {
    boost::asio::io_service ios;

    GST_INFO ("Connecting registrar");

    if (registrarAddress.size() >= 3 && registrarAddress.substr (0, 3) == "wss") {
      secureClient = std::shared_ptr<SecureWebSocketClient> (new
                     SecureWebSocketClient() );
      secureClient->clear_access_channels (websocketpp::log::alevel::all);
      secureClient->clear_error_channels (websocketpp::log::elevel::all);

      // Register our handlers
      secureClient->set_open_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                        std::shared_ptr<SecureWebSocketClient>,
                                        websocketpp::connection_hdl) ) &WebSocketRegistrar::connectionOpen, this,
                                      secureClient, std::placeholders::_1) );
      secureClient->set_message_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                           std::shared_ptr<SecureWebSocketClient>, websocketpp::connection_hdl,
                                           SecureWebSocketClient::message_ptr) ) &WebSocketRegistrar::receivedMessage,
                                         this, secureClient, std::placeholders::_1, std::placeholders::_2) );
      secureClient->set_close_handler (std::bind (&WebSocketRegistrar::closedHandler,
                                       this, std::placeholders::_1) );
      secureClient->set_tls_init_handler ( [] (websocketpp::connection_hdl hdl) ->
      context_ptr {
        context_ptr ctx (new boost::asio::ssl::context (boost::asio::ssl::context::tlsv1) );

        try {
          ctx->set_options (boost::asio::ssl::context::default_workarounds |
          boost::asio::ssl::context::no_sslv2 |
          boost::asio::ssl::context::single_dh_use);
        } catch (std::exception &e)
        {
          GST_ERROR ("Error setting tls on registrar connection: %s", e.what() );
        }

        return ctx;
      });

      // Initialize ASIO
      secureClient->init_asio (&ios);

      SecureWebSocketClient::connection_ptr con = secureClient->get_connection (
            registrarAddress, ec);

      if (con) {
        secure = true;
        secureClient->connect (con);
        secureClient->run();
      } else {
        GST_ERROR ("Cannot create connection %s", ec.message().c_str() );
      }
    } else {
      client = std::shared_ptr<WebSocketClient> (new WebSocketClient() );
      client->clear_access_channels (websocketpp::log::alevel::all);
      client->clear_error_channels (websocketpp::log::elevel::all);

      // Register our handlers
      client->set_open_handler (std::bind ( (void (WebSocketRegistrar::*) (
          std::shared_ptr<WebSocketClient>,
          websocketpp::connection_hdl) ) &WebSocketRegistrar::connectionOpen, this,
                                            client, std::placeholders::_1) );
      client->set_message_handler (std::bind ( (void (WebSocketRegistrar::*) (
                                     std::shared_ptr<WebSocketClient>, websocketpp::connection_hdl,
                                     WebSocketClient::message_ptr) ) &WebSocketRegistrar::receivedMessage, this,
                                   client, std::placeholders::_1, std::placeholders::_2) );
      client->set_close_handler (std::bind (&WebSocketRegistrar::closedHandler, this,
                                            std::placeholders::_1) );

      // Initialize ASIO
      client->init_asio (&ios);

      WebSocketClient::connection_ptr con = client->get_connection (registrarAddress,
                                            ec);

      if (con) {
        secure = false;
        client->connect (con);
        client->run();
      } else {
        GST_ERROR ("Cannot create connection %s", ec.message().c_str() );
      }
    }

    if (finished) {
      break;
    }

    GST_INFO ("Registrar disconnected, trying to reconnect in %" G_GINT64_FORMAT
              " ms", waitTime.count () );

    std::unique_lock<std::mutex> lock (mutex);
    cond.wait_for (lock, waitTime);
    lock.unlock ();

    if (waitTime < (MAX_WAIT_TIME / 2) ) {
      waitTime = waitTime * 2;
    } else {
      waitTime = MAX_WAIT_TIME;
    }
  }

  GST_INFO ("Terminating");
}

template <typename ClientType>
void
WebSocketRegistrar::connectionOpen (std::shared_ptr<ClientType> client,
                                    websocketpp::connection_hdl hdl)
{
  Json::Value req;
  Json::Value params;
  Json::FastWriter writer;
  std::string request;

  waitTime = DEFAULT_WAIT_TIME;
  connection = hdl;

  req["jsonrpc"] = "2.0";
  req["method"] = "register";

  if (localSecurePort > 0) {
    params["ws"] = "wss://" + localAddress + ":" + std::to_string (
                     localSecurePort) + "/" + path;
  } else {
    params["ws"] = "ws://" + localAddress + ":" + std::to_string (
                     localPort) + "/" + path;
  }

  req["params"] = params;

  request = writer.write (req);
  GST_DEBUG ("Registrar open, sending message: %s", request.c_str() );

  try {
    client->send (hdl, request, websocketpp::frame::opcode::TEXT);
  } catch (websocketpp::exception e) {
    GST_ERROR ("Cannot send message to remote");
  }
}

void
WebSocketRegistrar::closedHandler (websocketpp::connection_hdl hdl)
{
  GST_DEBUG ("Registrar closed");
}

template <typename ClientType>
void
WebSocketRegistrar::receivedMessage (std::shared_ptr<ClientType> client,
                                     websocketpp::connection_hdl hdl,
                                     typename ClientType::message_ptr msg)
{
  GST_DEBUG ("Message: %s", msg->get_payload().c_str() );
}

WebSocketRegistrar::StaticConstructor WebSocketRegistrar::staticConstructor;

WebSocketRegistrar::StaticConstructor::StaticConstructor()
{
  GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_DEFAULT_NAME, 0,
                           GST_DEFAULT_NAME);
}

} /* kurento */
<|endoftext|>


<commit_before>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
#include "uithread.hh"
#include "internal.hh"
#include "../configure.h"
#include <semaphore.h>
#include <stdlib.h>
#include <deque>
#include <set>

namespace Rapicorn {

class ServerConnectionSource;
typedef std::shared_ptr<ServerConnectionSource> ServerConnectionSourceP;

static DurableInstance<Aida::BaseConnectionP> global_server_connection; // automatically allocated and never destroyed

class ServerConnectionSource : public virtual EventSource {
  const char             *WHERE;
  PollFD                  pollfd_;
  bool                    last_seen_primary_, need_check_primary_;
  ServerConnectionSource (EventLoop &loop, Aida::BaseConnectionP connection) :
    WHERE ("Rapicorn::UIThread::ServerConnection"),
    last_seen_primary_ (false), need_check_primary_ (false)
  {
    assert (*global_server_connection == NULL);
    *global_server_connection = connection;
    assert (*global_server_connection != NULL);
    primary (false);
    pollfd_.fd = global_server_connection->get()->notify_fd();
    pollfd_.events = PollFD::IN;
    pollfd_.revents = 0;
  }
  friend class FriendAllocator<ServerConnectionSource>;
public:
  static ServerConnectionSourceP
  create (EventLoop &loop, Aida::BaseConnectionP connection)
  {
    ServerConnectionSourceP self = FriendAllocator<ServerConnectionSource>::make_shared (loop, connection);
    loop.add (self, EventLoop::PRIORITY_NORMAL);
    self->add_poll (&self->pollfd_);
    return self;
  }
private:
  ~ServerConnectionSource ()
  {
    remove_poll (&pollfd_);
    loop_remove();
  }
  virtual bool
  prepare (const LoopState &state, int64*)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  check (const LoopState &state)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    last_seen_primary_ = state.seen_primary;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  dispatch (const LoopState &state)
  {
    global_server_connection->get()->dispatch();
    if (need_check_primary_)
      {
        need_check_primary_ = false;
        loop_->exec_idle (check_primaries);
      }
    return true;
  }
  static void
  check_primaries()
  {
    // seen_primary is merely a hint, this handler checks the real loop state
    const bool uithread_finishable = uithread_main_loop()->finishable();
    if (uithread_finishable)
      ApplicationImpl::the().lost_primaries();
  }
};

struct Initializer {
  int *argcp; char **argv; const StringVector *args;
  Mutex mutex; Cond cond; bool done;
};

static ThreadInfo *volatile uithread_threadinfo = NULL;

class UIThread {
  std::thread             thread_;
  pthread_mutex_t         thread_mutex_;
  volatile bool           running_;
  Initializer            *idata_;
  const MainLoopP         main_loop_;
public:
  UIThread (Initializer *idata) :
    thread_mutex_ (PTHREAD_MUTEX_INITIALIZER), running_ (0), idata_ (idata),
    main_loop_ (MainLoop::create())
  {}
  bool  running() const { return running_; }
  void
  start()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.get_id() == std::thread::id())
      {
        assert (running_ == false);
        thread_ = std::thread (std::ref (*this));
      }
    pthread_mutex_unlock (&thread_mutex_);
  }
  void
  join()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.joinable())
      thread_.join();
    pthread_mutex_unlock (&thread_mutex_);
    assert (running_ == false);
  }
  void
  queue_stop()
  {
    pthread_mutex_lock (&thread_mutex_);
    main_loop_->quit();
    pthread_mutex_unlock (&thread_mutex_);
  }
  MainLoopP         main_loop()   { return main_loop_; }
private:
  ~UIThread ()
  {
    fatal ("UIThread singleton in dtor");
    // FIXME: leaking ServerConnectionSource ref count...
  }
  void
  initialize ()
  {
    assert_return (idata_ != NULL);
    // idata_core() already called
    ThisThread::affinity (string_to_int (string_vector_find_value (*idata_->args, "cpu-affinity=", "-1")));
    // initialize Application singleton
    ApplicationImpl &application = ApplicationImpl::the();
    // setup Aida server connection for RPC calls
    Aida::BaseConnectionP connection =
      Aida::ServerConnection::bind<ApplicationIface> ("inproc://Rapicorn-" RAPICORN_VERSION,
                                                      shared_ptr_cast<ApplicationIface> (&application));
    assert_return (NULL != &ApplicationImpl::the());
    // hook up server connection to main loop to process remote calls
    ServerConnectionSourceP server_source = ServerConnectionSource::create (*main_loop_, connection);
    // Complete initialization by signalling caller
    idata_->done = true;
    idata_->mutex.lock();
    idata_->cond.signal();
    idata_->mutex.unlock();
    idata_ = NULL;
  }
public:
  void
  operator() ()
  {
    assert (atomic_load (&uithread_threadinfo) == NULL);
    atomic_store (&uithread_threadinfo, &ThreadInfo::self());
    ThreadInfo::self().name ("RapicornUIThread");
    const bool running_twice = __sync_fetch_and_add (&running_, +1);
    assert (running_twice == false);

    initialize();
    assert_return (idata_ == NULL);
    main_loop_->run();
    WindowImpl::forcefully_close_all();
    DisplayDriver::forcefully_close_all();
    while (!main_loop_->finishable())
      if (!main_loop_->iterate (false))
        break;  // handle pending idle handlers like exec_*()
    main_loop_->destroy_loop();

    assert (running_ == true);
    const bool stopped_twice = !__sync_fetch_and_sub (&running_, +1);
    assert (stopped_twice == false);

    assert (running_ == false);
    ThreadInfo *last_threadinfo = atomic_swap (&uithread_threadinfo, (ThreadInfo*) NULL);
    assert (last_threadinfo == &ThreadInfo::self());
  }
};
static UIThread *the_uithread = NULL;

MainLoopP
uithread_main_loop ()
{
  return the_uithread ? the_uithread->main_loop() : NULL;
}

bool
uithread_is_current ()
{
  return atomic_load (&uithread_threadinfo) == &ThreadInfo::self();
}

static void
reap_uithread_atexit()
{
  if (the_uithread && the_uithread->running())
    RapicornInternal::uithread_shutdown();
}

} // Rapicorn

#include <rcore/testutils.hh>

namespace Rapicorn {

// === UI-Thread Syscalls ===
struct Callable {
  virtual int64 operator() () = 0;
  virtual      ~Callable   () {}
};
static std::deque<Callable*> syscall_queue;
static Mutex                 syscall_mutex;

static int64
ui_thread_syscall (Callable *callable)
{
  syscall_mutex.lock();
  syscall_queue.push_back (callable);
  syscall_mutex.unlock();
  const int64 result = RapicornInternal::client_app_test_hook();
  return result;
}

// === UI-Thread Test Runs ===
class SyscallTestRunner : public Callable {
  void (*test_runner) (void);
public:
  SyscallTestRunner (void (*func) (void)) : test_runner (func) {}
  int64 operator()  ()                    { test_runner(); return 0; }
};

static void
wrap_test_runner (void)
{
  Test::RegisterTest::test_set_trigger (uithread_test_trigger);
}

// === UI-Thread Test Trigger ===
class SyscallTestTrigger : public Callable {
  void (*test_func) (void);
public:
  SyscallTestTrigger (void (*tfunc) (void)) : test_func (tfunc) {}
  int64 operator()  ()                      { test_func(); return 0; }
};

void
uithread_test_trigger (void (*test_func) ())
{
  assert_return (test_func != NULL);
  assert_return (the_uithread != NULL);
  // run tests from ui-thread
  ui_thread_syscall (new SyscallTestTrigger (test_func));
  // ensure ui-thread shutdown
  RapicornInternal::uithread_shutdown();
}

} // Rapicorn

namespace RapicornInternal {

int64
server_app_test_hook()
{
  int64 result = -1;
  syscall_mutex.lock();
  while (syscall_queue.size())
    {
      Callable &callable = *syscall_queue.front();
      syscall_queue.pop_front();
      syscall_mutex.unlock();
      result = callable ();
      delete &callable;
      syscall_mutex.lock();
    }
  syscall_mutex.unlock();
  return result;
}

bool // provide errno on error
uithread_bootup (int *argcp, char **argv, const StringVector &args)
{
  assert_return (the_uithread == NULL, false);
  // catch exit() while UIThread is still running
  if (std::atexit (reap_uithread_atexit) != 0)
    fatal ("failed to install ui-thread reaper");
  // setup client/server connection pair
  Initializer idata;
  // initialize and create UIThread
  idata.argcp = argcp; idata.argv = argv; idata.args = &args; idata.done = false;
  // start and syncronize with thread
  idata.mutex.lock();
  the_uithread = new UIThread (&idata);
  the_uithread->start();
  while (!idata.done)
    idata.cond.wait (idata.mutex);
  idata.mutex.unlock();
  assert (the_uithread->running());
  // install handler for UIThread test cases
  wrap_test_runner();
  errno = 0;
  return true;
}

void
uithread_shutdown()
{
  if (the_uithread && the_uithread->running())
    {
      the_uithread->queue_stop(); // stops ui thread main loop
      the_uithread->join();
    }
}

} // RapicornInternal
<commit_msg>UI: catch calling exit() from within the uithread because it cannot join itself<commit_after>// This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
#include "uithread.hh"
#include "internal.hh"
#include "../configure.h"
#include <semaphore.h>
#include <stdlib.h>
#include <deque>
#include <set>

namespace Rapicorn {

class ServerConnectionSource;
typedef std::shared_ptr<ServerConnectionSource> ServerConnectionSourceP;

static DurableInstance<Aida::BaseConnectionP> global_server_connection; // automatically allocated and never destroyed

class ServerConnectionSource : public virtual EventSource {
  const char             *WHERE;
  PollFD                  pollfd_;
  bool                    last_seen_primary_, need_check_primary_;
  ServerConnectionSource (EventLoop &loop, Aida::BaseConnectionP connection) :
    WHERE ("Rapicorn::UIThread::ServerConnection"),
    last_seen_primary_ (false), need_check_primary_ (false)
  {
    assert (*global_server_connection == NULL);
    *global_server_connection = connection;
    assert (*global_server_connection != NULL);
    primary (false);
    pollfd_.fd = global_server_connection->get()->notify_fd();
    pollfd_.events = PollFD::IN;
    pollfd_.revents = 0;
  }
  friend class FriendAllocator<ServerConnectionSource>;
public:
  static ServerConnectionSourceP
  create (EventLoop &loop, Aida::BaseConnectionP connection)
  {
    ServerConnectionSourceP self = FriendAllocator<ServerConnectionSource>::make_shared (loop, connection);
    loop.add (self, EventLoop::PRIORITY_NORMAL);
    self->add_poll (&self->pollfd_);
    return self;
  }
private:
  ~ServerConnectionSource ()
  {
    remove_poll (&pollfd_);
    loop_remove();
  }
  virtual bool
  prepare (const LoopState &state, int64*)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  check (const LoopState &state)
  {
    if (UNLIKELY (last_seen_primary_ && !state.seen_primary))
      need_check_primary_ = true;
    last_seen_primary_ = state.seen_primary;
    return need_check_primary_ || global_server_connection->get()->pending();
  }
  virtual bool
  dispatch (const LoopState &state)
  {
    global_server_connection->get()->dispatch();
    if (need_check_primary_)
      {
        need_check_primary_ = false;
        loop_->exec_idle (check_primaries);
      }
    return true;
  }
  static void
  check_primaries()
  {
    // seen_primary is merely a hint, this handler checks the real loop state
    const bool uithread_finishable = uithread_main_loop()->finishable();
    if (uithread_finishable)
      ApplicationImpl::the().lost_primaries();
  }
};

struct Initializer {
  int *argcp; char **argv; const StringVector *args;
  Mutex mutex; Cond cond; bool done;
};

static ThreadInfo *volatile uithread_threadinfo = NULL;

class UIThread {
  std::thread             thread_;
  pthread_mutex_t         thread_mutex_;
  volatile bool           running_;
  Initializer            *idata_;
  const MainLoopP         main_loop_;
public:
  UIThread (Initializer *idata) :
    thread_mutex_ (PTHREAD_MUTEX_INITIALIZER), running_ (0), idata_ (idata),
    main_loop_ (MainLoop::create())
  {}
  bool  running() const { return running_; }
  void
  start()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.get_id() == std::thread::id())
      {
        assert (running_ == false);
        thread_ = std::thread (std::ref (*this));
      }
    pthread_mutex_unlock (&thread_mutex_);
  }
  bool
  is_self()
  {
    pthread_mutex_lock (&thread_mutex_);
    bool isself = thread_.native_handle() == pthread_self();
    pthread_mutex_unlock (&thread_mutex_);
    return isself;
  }
  void
  join()
  {
    pthread_mutex_lock (&thread_mutex_);
    if (thread_.joinable())
      thread_.join();
    pthread_mutex_unlock (&thread_mutex_);
    assert (running_ == false);
  }
  void
  queue_stop()
  {
    pthread_mutex_lock (&thread_mutex_);
    main_loop_->quit();
    pthread_mutex_unlock (&thread_mutex_);
  }
  MainLoopP         main_loop()   { return main_loop_; }
private:
  ~UIThread ()
  {
    fatal ("UIThread singleton in dtor");
    // FIXME: leaking ServerConnectionSource ref count...
  }
  void
  initialize ()
  {
    assert_return (idata_ != NULL);
    // idata_core() already called
    ThisThread::affinity (string_to_int (string_vector_find_value (*idata_->args, "cpu-affinity=", "-1")));
    // initialize Application singleton
    ApplicationImpl &application = ApplicationImpl::the();
    // setup Aida server connection for RPC calls
    Aida::BaseConnectionP connection =
      Aida::ServerConnection::bind<ApplicationIface> ("inproc://Rapicorn-" RAPICORN_VERSION,
                                                      shared_ptr_cast<ApplicationIface> (&application));
    assert_return (NULL != &ApplicationImpl::the());
    // hook up server connection to main loop to process remote calls
    ServerConnectionSourceP server_source = ServerConnectionSource::create (*main_loop_, connection);
    // Complete initialization by signalling caller
    idata_->done = true;
    idata_->mutex.lock();
    idata_->cond.signal();
    idata_->mutex.unlock();
    idata_ = NULL;
  }
public:
  void
  operator() ()
  {
    assert (atomic_load (&uithread_threadinfo) == NULL);
    atomic_store (&uithread_threadinfo, &ThreadInfo::self());
    ThreadInfo::self().name ("RapicornUIThread");
    const bool running_twice = __sync_fetch_and_add (&running_, +1);
    assert (running_twice == false);

    initialize();
    assert_return (idata_ == NULL);
    main_loop_->run();
    WindowImpl::forcefully_close_all();
    DisplayDriver::forcefully_close_all();
    while (!main_loop_->finishable())
      if (!main_loop_->iterate (false))
        break;  // handle pending idle handlers like exec_*()
    main_loop_->destroy_loop();

    assert (running_ == true);
    const bool stopped_twice = !__sync_fetch_and_sub (&running_, +1);
    assert (stopped_twice == false);

    assert (running_ == false);
    ThreadInfo *last_threadinfo = atomic_swap (&uithread_threadinfo, (ThreadInfo*) NULL);
    assert (last_threadinfo == &ThreadInfo::self());
  }
};
static UIThread *the_uithread = NULL;

MainLoopP
uithread_main_loop ()
{
  return the_uithread ? the_uithread->main_loop() : NULL;
}

bool
uithread_is_current ()
{
  return atomic_load (&uithread_threadinfo) == &ThreadInfo::self();
}

static void
reap_uithread_atexit()
{
  if (the_uithread && the_uithread->running())
    RapicornInternal::uithread_shutdown();
}

} // Rapicorn

#include <rcore/testutils.hh>

namespace Rapicorn {

// === UI-Thread Syscalls ===
struct Callable {
  virtual int64 operator() () = 0;
  virtual      ~Callable   () {}
};
static std::deque<Callable*> syscall_queue;
static Mutex                 syscall_mutex;

static int64
ui_thread_syscall (Callable *callable)
{
  syscall_mutex.lock();
  syscall_queue.push_back (callable);
  syscall_mutex.unlock();
  const int64 result = RapicornInternal::client_app_test_hook();
  return result;
}

// === UI-Thread Test Runs ===
class SyscallTestRunner : public Callable {
  void (*test_runner) (void);
public:
  SyscallTestRunner (void (*func) (void)) : test_runner (func) {}
  int64 operator()  ()                    { test_runner(); return 0; }
};

static void
wrap_test_runner (void)
{
  Test::RegisterTest::test_set_trigger (uithread_test_trigger);
}

// === UI-Thread Test Trigger ===
class SyscallTestTrigger : public Callable {
  void (*test_func) (void);
public:
  SyscallTestTrigger (void (*tfunc) (void)) : test_func (tfunc) {}
  int64 operator()  ()                      { test_func(); return 0; }
};

void
uithread_test_trigger (void (*test_func) ())
{
  assert_return (test_func != NULL);
  assert_return (the_uithread != NULL);
  // run tests from ui-thread
  ui_thread_syscall (new SyscallTestTrigger (test_func));
  // ensure ui-thread shutdown
  RapicornInternal::uithread_shutdown();
}

} // Rapicorn

namespace RapicornInternal {

int64
server_app_test_hook()
{
  int64 result = -1;
  syscall_mutex.lock();
  while (syscall_queue.size())
    {
      Callable &callable = *syscall_queue.front();
      syscall_queue.pop_front();
      syscall_mutex.unlock();
      result = callable ();
      delete &callable;
      syscall_mutex.lock();
    }
  syscall_mutex.unlock();
  return result;
}

bool // provide errno on error
uithread_bootup (int *argcp, char **argv, const StringVector &args)
{
  assert_return (the_uithread == NULL, false);
  // catch exit() while UIThread is still running
  if (std::atexit (reap_uithread_atexit) != 0)
    fatal ("failed to install ui-thread reaper");
  // setup client/server connection pair
  Initializer idata;
  // initialize and create UIThread
  idata.argcp = argcp; idata.argv = argv; idata.args = &args; idata.done = false;
  // start and syncronize with thread
  idata.mutex.lock();
  the_uithread = new UIThread (&idata);
  the_uithread->start();
  while (!idata.done)
    idata.cond.wait (idata.mutex);
  idata.mutex.unlock();
  assert (the_uithread->running());
  // install handler for UIThread test cases
  wrap_test_runner();
  errno = 0;
  return true;
}

void
uithread_shutdown()
{
  if (the_uithread && the_uithread->running())
    {
      the_uithread->queue_stop(); // stops ui thread main loop
      if (the_uithread->is_self())
        fatal ("RapicornInternal::%s: uithread shutdown called from within Rapicorn uithread", __func__);
      the_uithread->join();
    }
}

} // RapicornInternal
<|endoftext|>


<commit_before>/*===================================================================
QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Waypoint class
 *
 *   @author Benjamin Knecht <mavteam@student.ethz.ch>
 *   @author Petri Tanskanen <mavteam@student.ethz.ch>
 *
 */

#include <QStringList>
#include <QDebug>

#include "Waypoint.h"

Waypoint::Waypoint(quint16 _id, double _x, double _y, double _z, double _param1, double _param2, double _param3, double _param4,
                   bool _autocontinue, bool _current, MAV_FRAME _frame, MAV_CMD _action, const QString& _description)
    : id(_id),
      x(_x),
      y(_y),
      z(_z),
      yaw(_param4),
      frame(_frame),
      action(_action),
      autocontinue(_autocontinue),
      current(_current),
      orbit(_param3),
      param1(_param1),
      param2(_param2),
      name(QString("WP%1").arg(id, 2, 10, QChar('0'))),
      description(_description),
      reachedTime(0)
{
}

Waypoint::~Waypoint()
{
}

bool Waypoint::isNavigationType()
{
    return (action < MAV_CMD_NAV_LAST);
}

void Waypoint::save(QTextStream &saveStream)
{
    QString position("%1\t%2\t%3");
    position = position.arg(x, 0, 'g', 18);
    position = position.arg(y, 0, 'g', 18);
    position = position.arg(z, 0, 'g', 18);
    QString parameters("%1\t%2\t%3\t%4");
    parameters = parameters.arg(param1, 0, 'g', 18).arg(param2, 0, 'g', 18).arg(orbit, 0, 'g', 18).arg(yaw, 0, 'g', 18);
    // FORMAT: <INDEX> <CURRENT WP> <COORD FRAME> <COMMAND> <PARAM1> <PARAM2> <PARAM3> <PARAM4> <PARAM5/X/LONGITUDE> <PARAM6/Y/LATITUDE> <PARAM7/Z/ALTITUDE> <AUTOCONTINUE> <DESCRIPTION>
    // as documented here: http://qgroundcontrol.org/waypoint_protocol
    saveStream << this->getId() << "\t" << this->getCurrent() << "\t" << this->getFrame() << "\t" << this->getAction() << "\t"  << parameters << "\t" << position  << "\t" << this->getAutoContinue() << "\r\n"; //"\t" << this->getDescription() << "\r\n";
}

bool Waypoint::load(QTextStream &loadStream)
{
    const QStringList &wpParams = loadStream.readLine().split("\t");
    if (wpParams.size() == 12) {
        this->id = wpParams[0].toInt();
        this->current = (wpParams[1].toInt() == 1 ? true : false);
        this->frame = (MAV_FRAME) wpParams[2].toInt();
        this->action = (MAV_CMD) wpParams[3].toInt();
        this->param1 = wpParams[4].toDouble();
        this->param2 = wpParams[5].toDouble();
        this->orbit = wpParams[6].toDouble();
        this->yaw = wpParams[7].toDouble();
        this->x = wpParams[8].toDouble();
        this->y = wpParams[9].toDouble();
        this->z = wpParams[10].toDouble();
        this->autocontinue = (wpParams[11].toInt() == 1 ? true : false);
        //this->description = wpParams[12];
        return true;
    }
    return false;
}

void Waypoint::setId(quint16 id)
{
    this->id = id;
    this->name = QString("WP%1").arg(id, 2, 10, QChar('0'));
    emit changed(this);
}

void Waypoint::setX(double x)
{
    if (!isinf(x) && !isnan(x) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->x = x;
        emit changed(this);
    }
}

void Waypoint::setY(double y)
{
    if (!isinf(y) && !isnan(y) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->y = y;
        emit changed(this);
    }
}

void Waypoint::setZ(double z)
{
    if (!isinf(z) && !isnan(z) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->z = z;
        emit changed(this);
    }
}

void Waypoint::setLatitude(double lat)
{
    if (this->x != lat && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->x = lat;
        emit changed(this);
    }
}

void Waypoint::setLongitude(double lon)
{
    if (this->y != lon && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->y = lon;
        emit changed(this);
    }
}

void Waypoint::setAltitude(double altitude)
{
    if (this->z != altitude && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->z = altitude;
        emit changed(this);
    }
}

void Waypoint::setYaw(int yaw)
{
    if (this->yaw != yaw)
    {
        this->yaw = yaw;
        emit changed(this);
    }
}

void Waypoint::setYaw(double yaw)
{
    if (this->yaw != yaw)
    {
        this->yaw = yaw;
        emit changed(this);
    }
}

void Waypoint::setAction(int action)
{
    if (this->action != (MAV_CMD)action)
    {
        this->action = (MAV_CMD)action;
        emit changed(this);
    }
}

void Waypoint::setAction(MAV_CMD action)
{
    if (this->action != action) {
        this->action = action;

        // Flick defaults according to WP type

        switch (this->action) {
            case MAV_CMD_NAV_TAKEOFF:
            // We default to 15 degrees minimum takeoff pitch
            this->param1 = 15.0;
            break;
        }

        emit changed(this);
    }
}

void Waypoint::setFrame(MAV_FRAME frame)
{
    if (this->frame != frame) {
        this->frame = frame;
        emit changed(this);
    }
}

void Waypoint::setAutocontinue(bool autoContinue)
{
    if (this->autocontinue != autoContinue) {
        this->autocontinue = autoContinue;
        emit changed(this);
    }
}

void Waypoint::setCurrent(bool current)
{
    if (this->current != current)
    {
        this->current = current;
        // The current waypoint index is handled by the list
        // and not part of the individual waypoint update state
    }
}

void Waypoint::setAcceptanceRadius(double radius)
{
    if (this->param2 != radius)
    {
        this->param2 = radius;
        emit changed(this);
    }
}

void Waypoint::setParam1(double param1)
{
    //// // qDebug() << "SENDER:" << QObject::sender();
    //// // qDebug() << "PARAM1 SET REQ:" << param1;
    if (this->param1 != param1)
    {
        this->param1 = param1;
        emit changed(this);
    }
}

void Waypoint::setParam2(double param2)
{
    if (this->param2 != param2)
    {
        this->param2 = param2;
        emit changed(this);
    }
}

void Waypoint::setParam3(double param3)
{
    if (this->orbit != param3) {
        this->orbit = param3;
        emit changed(this);
    }
}

void Waypoint::setParam4(double param4)
{
    if (this->yaw != param4) {
        this->yaw = param4;
        emit changed(this);
    }
}

void Waypoint::setParam5(double param5)
{
    if (this->x != param5) {
        this->x = param5;
        emit changed(this);
    }
}

void Waypoint::setParam6(double param6)
{
    if (this->y != param6) {
        this->y = param6;
        emit changed(this);
    }
}

void Waypoint::setParam7(double param7)
{
    if (this->z != param7) {
        this->z = param7;
        emit changed(this);
    }
}

void Waypoint::setLoiterOrbit(double orbit)
{
    if (this->orbit != orbit) {
        this->orbit = orbit;
        emit changed(this);
    }
}

void Waypoint::setHoldTime(int holdTime)
{
    if (this->param1 != holdTime) {
        this->param1 = holdTime;
        emit changed(this);
    }
}

void Waypoint::setHoldTime(double holdTime)
{
    if (this->param1 != holdTime) {
        this->param1 = holdTime;
        emit changed(this);
    }
}

void Waypoint::setTurns(int turns)
{
    if (this->param1 != turns) {
        this->param1 = turns;
        emit changed(this);
    }
}
<commit_msg>Fix warning in waypoint class<commit_after>/*===================================================================
QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Waypoint class
 *
 *   @author Benjamin Knecht <mavteam@student.ethz.ch>
 *   @author Petri Tanskanen <mavteam@student.ethz.ch>
 *
 */

#include <QStringList>
#include <QDebug>

#include "Waypoint.h"

Waypoint::Waypoint(quint16 _id, double _x, double _y, double _z, double _param1, double _param2, double _param3, double _param4,
                   bool _autocontinue, bool _current, MAV_FRAME _frame, MAV_CMD _action, const QString& _description)
    : id(_id),
      x(_x),
      y(_y),
      z(_z),
      yaw(_param4),
      frame(_frame),
      action(_action),
      autocontinue(_autocontinue),
      current(_current),
      orbit(_param3),
      param1(_param1),
      param2(_param2),
      name(QString("WP%1").arg(id, 2, 10, QChar('0'))),
      description(_description),
      reachedTime(0)
{
}

Waypoint::~Waypoint()
{
}

bool Waypoint::isNavigationType()
{
    return (action < MAV_CMD_NAV_LAST);
}

void Waypoint::save(QTextStream &saveStream)
{
    QString position("%1\t%2\t%3");
    position = position.arg(x, 0, 'g', 18);
    position = position.arg(y, 0, 'g', 18);
    position = position.arg(z, 0, 'g', 18);
    QString parameters("%1\t%2\t%3\t%4");
    parameters = parameters.arg(param1, 0, 'g', 18).arg(param2, 0, 'g', 18).arg(orbit, 0, 'g', 18).arg(yaw, 0, 'g', 18);
    // FORMAT: <INDEX> <CURRENT WP> <COORD FRAME> <COMMAND> <PARAM1> <PARAM2> <PARAM3> <PARAM4> <PARAM5/X/LONGITUDE> <PARAM6/Y/LATITUDE> <PARAM7/Z/ALTITUDE> <AUTOCONTINUE> <DESCRIPTION>
    // as documented here: http://qgroundcontrol.org/waypoint_protocol
    saveStream << this->getId() << "\t" << this->getCurrent() << "\t" << this->getFrame() << "\t" << this->getAction() << "\t"  << parameters << "\t" << position  << "\t" << this->getAutoContinue() << "\r\n"; //"\t" << this->getDescription() << "\r\n";
}

bool Waypoint::load(QTextStream &loadStream)
{
    const QStringList &wpParams = loadStream.readLine().split("\t");
    if (wpParams.size() == 12) {
        this->id = wpParams[0].toInt();
        this->current = (wpParams[1].toInt() == 1 ? true : false);
        this->frame = (MAV_FRAME) wpParams[2].toInt();
        this->action = (MAV_CMD) wpParams[3].toInt();
        this->param1 = wpParams[4].toDouble();
        this->param2 = wpParams[5].toDouble();
        this->orbit = wpParams[6].toDouble();
        this->yaw = wpParams[7].toDouble();
        this->x = wpParams[8].toDouble();
        this->y = wpParams[9].toDouble();
        this->z = wpParams[10].toDouble();
        this->autocontinue = (wpParams[11].toInt() == 1 ? true : false);
        //this->description = wpParams[12];
        return true;
    }
    return false;
}

void Waypoint::setId(quint16 id)
{
    this->id = id;
    this->name = QString("WP%1").arg(id, 2, 10, QChar('0'));
    emit changed(this);
}

void Waypoint::setX(double x)
{
    if (!isinf(x) && !isnan(x) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->x = x;
        emit changed(this);
    }
}

void Waypoint::setY(double y)
{
    if (!isinf(y) && !isnan(y) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->y = y;
        emit changed(this);
    }
}

void Waypoint::setZ(double z)
{
    if (!isinf(z) && !isnan(z) && ((this->frame == MAV_FRAME_LOCAL_NED) || (this->frame == MAV_FRAME_LOCAL_ENU)))
    {
        this->z = z;
        emit changed(this);
    }
}

void Waypoint::setLatitude(double lat)
{
    if (this->x != lat && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->x = lat;
        emit changed(this);
    }
}

void Waypoint::setLongitude(double lon)
{
    if (this->y != lon && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->y = lon;
        emit changed(this);
    }
}

void Waypoint::setAltitude(double altitude)
{
    if (this->z != altitude && ((this->frame == MAV_FRAME_GLOBAL) || (this->frame == MAV_FRAME_GLOBAL_RELATIVE_ALT)))
    {
        this->z = altitude;
        emit changed(this);
    }
}

void Waypoint::setYaw(int yaw)
{
    if (this->yaw != yaw)
    {
        this->yaw = yaw;
        emit changed(this);
    }
}

void Waypoint::setYaw(double yaw)
{
    if (this->yaw != yaw)
    {
        this->yaw = yaw;
        emit changed(this);
    }
}

void Waypoint::setAction(int action)
{
    if (this->action != (MAV_CMD)action)
    {
        this->action = (MAV_CMD)action;
        emit changed(this);
    }
}

void Waypoint::setAction(MAV_CMD action)
{
    if (this->action != action) {
        this->action = action;

        // Flick defaults according to WP type

        if (this->action == MAV_CMD_NAV_TAKEOFF) {
            // We default to 15 degrees minimum takeoff pitch
            this->param1 = 15.0;
        }

        emit changed(this);
    }
}

void Waypoint::setFrame(MAV_FRAME frame)
{
    if (this->frame != frame) {
        this->frame = frame;
        emit changed(this);
    }
}

void Waypoint::setAutocontinue(bool autoContinue)
{
    if (this->autocontinue != autoContinue) {
        this->autocontinue = autoContinue;
        emit changed(this);
    }
}

void Waypoint::setCurrent(bool current)
{
    if (this->current != current)
    {
        this->current = current;
        // The current waypoint index is handled by the list
        // and not part of the individual waypoint update state
    }
}

void Waypoint::setAcceptanceRadius(double radius)
{
    if (this->param2 != radius)
    {
        this->param2 = radius;
        emit changed(this);
    }
}

void Waypoint::setParam1(double param1)
{
    //// // qDebug() << "SENDER:" << QObject::sender();
    //// // qDebug() << "PARAM1 SET REQ:" << param1;
    if (this->param1 != param1)
    {
        this->param1 = param1;
        emit changed(this);
    }
}

void Waypoint::setParam2(double param2)
{
    if (this->param2 != param2)
    {
        this->param2 = param2;
        emit changed(this);
    }
}

void Waypoint::setParam3(double param3)
{
    if (this->orbit != param3) {
        this->orbit = param3;
        emit changed(this);
    }
}

void Waypoint::setParam4(double param4)
{
    if (this->yaw != param4) {
        this->yaw = param4;
        emit changed(this);
    }
}

void Waypoint::setParam5(double param5)
{
    if (this->x != param5) {
        this->x = param5;
        emit changed(this);
    }
}

void Waypoint::setParam6(double param6)
{
    if (this->y != param6) {
        this->y = param6;
        emit changed(this);
    }
}

void Waypoint::setParam7(double param7)
{
    if (this->z != param7) {
        this->z = param7;
        emit changed(this);
    }
}

void Waypoint::setLoiterOrbit(double orbit)
{
    if (this->orbit != orbit) {
        this->orbit = orbit;
        emit changed(this);
    }
}

void Waypoint::setHoldTime(int holdTime)
{
    if (this->param1 != holdTime) {
        this->param1 = holdTime;
        emit changed(this);
    }
}

void Waypoint::setHoldTime(double holdTime)
{
    if (this->param1 != holdTime) {
        this->param1 = holdTime;
        emit changed(this);
    }
}

void Waypoint::setTurns(int turns)
{
    if (this->param1 != turns) {
        this->param1 = turns;
        emit changed(this);
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
//

// Author: ericv@google.com (Eric Veach)

#include "s2/s2edge_tessellator.h"

#include <gtest/gtest.h>
#include "s2/s2edge_distances.h"
#include "s2/s2loop.h"
#include "s2/s2pointutil.h"
#include "s2/s2projections.h"
#include "s2/s2testing.h"
#include "s2/s2text_format.h"

using s2textformat::ParsePointsOrDie;
using std::fabs;
using std::min;
using std::max;
using std::vector;

namespace {

class DistStats {
 public:
  DistStats() : min_dist_(S1Angle::Infinity()),
                max_dist_(S1Angle::Zero()),
                sum_dist_(S1Angle::Zero()),
                count_(0) {
  }
  void Tally(S1Angle dist) {
    min_dist_ = std::min(dist, min_dist_);
    max_dist_ = std::max(dist, max_dist_);
    sum_dist_ += dist;
    count_ += 1;
  }
  S1Angle min_dist() const { return min_dist_; }
  S1Angle max_dist() const { return max_dist_; }
  S1Angle avg_dist() const { return sum_dist_ / count_; }

 private:
  S1Angle min_dist_, max_dist_, sum_dist_;
  int count_;
};

S1Angle GetMaxDistance(const S2::Projection& proj,
                       const R2Point& px, const S2Point& x,
                       const R2Point& py, const S2Point& y) {
  // Step along the projected edge at a fine resolution and keep track of the
  // maximum distance of any point to the current geodesic edge.
  const int kNumSteps = 100;
  S1ChordAngle max_dist = S1ChordAngle::Zero();
  for (double f = 0.5 / kNumSteps; f < 1.0; f += 1.0 / kNumSteps) {
    S1ChordAngle dist = S1ChordAngle::Infinity();
    S2Point p = proj.Unproject(proj.Interpolate(f, px, py));
    S2::UpdateMinDistance(p, x, y, &dist);
    if (dist > max_dist) max_dist = dist;
  }
  return max_dist.ToAngle();
}

DistStats TestUnprojected(const S2::Projection& proj, S1Angle tolerance,
                          const R2Point& pa, const R2Point& pb) {
  S2EdgeTessellator tess(&proj, tolerance);
  vector<S2Point> vertices;
  tess.AppendUnprojected(pa, pb, &vertices);
  EXPECT_TRUE(S2::ApproxEquals(proj.Unproject(pa), vertices.front()));
  EXPECT_TRUE(S2::ApproxEquals(proj.Unproject(pb), vertices.back()));
  DistStats stats;
  if (pa == pb) {
    EXPECT_EQ(1, vertices.size());
    return stats;
  }
  // Precompute the normal to the projected edge.
  Vector2_d norm = (pb - pa).Ortho().Normalize();
  S2Point x = vertices[0];
  R2Point px = proj.Project(x);
  for (int i = 1; i < vertices.size(); ++i) {
    S2Point y = vertices[i];
    R2Point py = proj.Project(y);
    // Check that every vertex is on the projected edge.
    EXPECT_LT((py - pa).DotProd(norm), 1e-14 * py.Norm());
    stats.Tally(GetMaxDistance(proj, px, x, py, y));
    x = y;
    px = py;
  }
  S2_LOG(INFO) << vertices.size() << " vertices, min/avg/max tolerance ratio = "
            << (stats.min_dist() / tolerance) << " / "
            << (stats.avg_dist() / tolerance) << " / "
            << (stats.max_dist() / tolerance);
  return stats;
}

DistStats TestProjected(const S2::Projection& proj, S1Angle tolerance,
                        const S2Point& a, const S2Point& b) {
  S2EdgeTessellator tess(&proj, tolerance);
  vector<R2Point> vertices;
  tess.AppendProjected(a, b, &vertices);
  EXPECT_TRUE(S2::ApproxEquals(a, proj.Unproject(vertices.front())));
  EXPECT_TRUE(S2::ApproxEquals(b, proj.Unproject(vertices.back())));
  DistStats stats;
  if (a == b) {
    EXPECT_EQ(1, vertices.size());
    return stats;
  }
  R2Point px = vertices[0];
  S2Point x = proj.Unproject(px);
  for (int i = 1; i < vertices.size(); ++i) {
    R2Point py = vertices[i];
    S2Point y = proj.Unproject(py);
    // Check that every vertex is on the geodesic edge.
    static S1ChordAngle kMaxInterpolationError(S1Angle::Radians(1e-14));
    EXPECT_TRUE(S2::IsDistanceLess(y, a, b, kMaxInterpolationError));
    stats.Tally(GetMaxDistance(proj, px, x, py, y));
    x = y;
    px = py;
  }
  S2_LOG(INFO) << vertices.size() << " vertices, min/avg/max tolerance ratio = "
            << (stats.min_dist() / tolerance) << " / "
            << (stats.avg_dist() / tolerance) << " / "
            << (stats.max_dist() / tolerance);
  return stats;
}

TEST(S2EdgeTessellator, ProjectedNoTessellation) {
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<R2Point> vertices;
  tess.AppendProjected(S2Point(1, 0, 0), S2Point(0, 1, 0), &vertices);
  EXPECT_EQ(2, vertices.size());
}

TEST(S2EdgeTessellator, UnprojectedNoTessellation) {
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  tess.AppendUnprojected(R2Point(0, 30), R2Point(0, 50), &vertices);
  EXPECT_EQ(2, vertices.size());
}

TEST(S2EdgeTessellator, UnprojectedWrapping) {
  // This tests that a projected edge that crosses the 180 degree meridian
  // goes the "short way" around the sphere.

  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  tess.AppendUnprojected(R2Point(-170, 0), R2Point(170, 80), &vertices);
  for (const auto& v : vertices) {
    EXPECT_GE(fabs(S2LatLng::Longitude(v).degrees()), 170);
  }
}

TEST(S2EdgeTessellator, ProjectedWrapping) {
  // This tests projecting a geodesic edge that crosses the 180 degree
  // meridian.  This results in a set of vertices that may be non-canonical
  // (i.e., absolute longitudes greater than 180 degrees) but that don't have
  // any sudden jumps in value, which is convenient for interpolating them.
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<R2Point> vertices;
  tess.AppendProjected(S2LatLng::FromDegrees(0, -170).ToPoint(),
                       S2LatLng::FromDegrees(0, 170).ToPoint(), &vertices);
  for (const auto& v : vertices) {
    EXPECT_LE(v.x(), -170);
  }
}

TEST(S2EdgeTessellator, UnprojectedWrappingMultipleCrossings) {
  // Tests an edge chain that crosses the 180 degree meridian multiple times.
  // Note that due to coordinate wrapping, the last vertex of one edge may not
  // exactly match the first edge of the next edge after unprojection.
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  for (double lat = 1; lat <= 60; ++lat) {
    tess.AppendUnprojected(R2Point(180 - 0.03 * lat, lat),
                           R2Point(-180 + 0.07 * lat, lat), &vertices);
    tess.AppendUnprojected(R2Point(-180 + 0.07 * lat, lat),
                           R2Point(180 - 0.03 * (lat + 1), lat + 1), &vertices);
  }
  for (const auto& v : vertices) {
    EXPECT_GE(fabs(S2LatLng::Longitude(v).degrees()), 175);
  }
}

TEST(S2EdgeTessellator, ProjectedWrappingMultipleCrossings) {
  // The following loop crosses the 180 degree meridian four times (twice in
  // each direction).
  auto loop = ParsePointsOrDie("0:160, 0:-40, 0:120, 0:-80, 10:120, "
                               "10:-40, 0:160");
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance(S1Angle::E7(1));
  S2EdgeTessellator tess(&proj, tolerance);
  vector<R2Point> vertices;
  for (int i = 0; i + 1 < loop.size(); ++i) {
    tess.AppendProjected(loop[i], loop[i + 1], &vertices);
  }
  EXPECT_EQ(vertices.front(), vertices.back());

  // Note that the R2Point coordinates are in (lng, lat) order.
  double min_lng = vertices[0].x();
  double max_lng = vertices[0].x();
  for (const R2Point& v : vertices) {
    min_lng = min(min_lng, v.x());
    max_lng = max(max_lng, v.x());
  }
  EXPECT_EQ(160, min_lng);
  EXPECT_EQ(640, max_lng);
}

TEST(S2EdgeTessellator, UnprojectedAccuracy) {
  S2::MercatorProjection proj(180);
  S1Angle tolerance(S1Angle::Degrees(1e-5));
  R2Point pa(0, 0), pb(89.999999, 179);
  DistStats stats = TestUnprojected(proj, tolerance, pa, pb);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, ProjectedAccuracy) {
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance(S1Angle::E7(1));
  S2Point a = S2LatLng::FromDegrees(-89.999, -170).ToPoint();
  S2Point b = S2LatLng::FromDegrees(50, 100).ToPoint();
  DistStats stats = TestProjected(proj, tolerance, a, b);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, ProjectedAccuracySeattleToNewYork) {
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance = S2Testing::MetersToAngle(1.23);
  S2Point seattle(S2LatLng::FromDegrees(47.6062, -122.3321).ToPoint());
  S2Point newyork(S2LatLng::FromDegrees(40.7128, -74.0059).ToPoint());
  DistStats stats = TestProjected(proj, tolerance, seattle, newyork);
  EXPECT_LT(stats.max_dist(), tolerance);
}

}  // namespace
<commit_msg>Add disabed equator-crossing tests<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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.
//

// Author: ericv@google.com (Eric Veach)

#include "s2/s2edge_tessellator.h"

#include <gtest/gtest.h>
#include "s2/s2edge_distances.h"
#include "s2/s2loop.h"
#include "s2/s2pointutil.h"
#include "s2/s2projections.h"
#include "s2/s2testing.h"
#include "s2/s2text_format.h"

using s2textformat::ParsePointsOrDie;
using std::fabs;
using std::min;
using std::max;
using std::vector;

namespace {

class DistStats {
 public:
  DistStats() : min_dist_(S1Angle::Infinity()),
                max_dist_(S1Angle::Zero()),
                sum_dist_(S1Angle::Zero()),
                count_(0) {
  }
  void Tally(S1Angle dist) {
    min_dist_ = std::min(dist, min_dist_);
    max_dist_ = std::max(dist, max_dist_);
    sum_dist_ += dist;
    count_ += 1;
  }
  S1Angle min_dist() const { return min_dist_; }
  S1Angle max_dist() const { return max_dist_; }
  S1Angle avg_dist() const { return sum_dist_ / count_; }

 private:
  S1Angle min_dist_, max_dist_, sum_dist_;
  int count_;
};

S1Angle GetMaxDistance(const S2::Projection& proj,
                       const R2Point& px, const S2Point& x,
                       const R2Point& py, const S2Point& y) {
  // Step along the projected edge at a fine resolution and keep track of the
  // maximum distance of any point to the current geodesic edge.
  const int kNumSteps = 100;
  S1ChordAngle max_dist = S1ChordAngle::Zero();
  for (double f = 0.5 / kNumSteps; f < 1.0; f += 1.0 / kNumSteps) {
    S1ChordAngle dist = S1ChordAngle::Infinity();
    S2Point p = proj.Unproject(proj.Interpolate(f, px, py));
    S2::UpdateMinDistance(p, x, y, &dist);
    if (dist > max_dist) max_dist = dist;
  }
  return max_dist.ToAngle();
}

DistStats TestUnprojected(const S2::Projection& proj, S1Angle tolerance,
                          const R2Point& pa, const R2Point& pb) {
  S2EdgeTessellator tess(&proj, tolerance);
  vector<S2Point> vertices;
  tess.AppendUnprojected(pa, pb, &vertices);
  EXPECT_TRUE(S2::ApproxEquals(proj.Unproject(pa), vertices.front()));
  EXPECT_TRUE(S2::ApproxEquals(proj.Unproject(pb), vertices.back()));
  DistStats stats;
  if (pa == pb) {
    EXPECT_EQ(1, vertices.size());
    return stats;
  }
  // Precompute the normal to the projected edge.
  Vector2_d norm = (pb - pa).Ortho().Normalize();
  S2Point x = vertices[0];
  R2Point px = proj.Project(x);
  for (int i = 1; i < vertices.size(); ++i) {
    S2Point y = vertices[i];
    R2Point py = proj.Project(y);
    // Check that every vertex is on the projected edge.
    EXPECT_LT((py - pa).DotProd(norm), 1e-14 * py.Norm());
    stats.Tally(GetMaxDistance(proj, px, x, py, y));
    x = y;
    px = py;
  }
  S2_LOG(INFO) << vertices.size() << " vertices, min/avg/max tolerance ratio = "
            << (stats.min_dist() / tolerance) << " / "
            << (stats.avg_dist() / tolerance) << " / "
            << (stats.max_dist() / tolerance);
  return stats;
}

DistStats TestProjected(const S2::Projection& proj, S1Angle tolerance,
                        const S2Point& a, const S2Point& b) {
  S2EdgeTessellator tess(&proj, tolerance);
  vector<R2Point> vertices;
  tess.AppendProjected(a, b, &vertices);
  EXPECT_TRUE(S2::ApproxEquals(a, proj.Unproject(vertices.front())));
  EXPECT_TRUE(S2::ApproxEquals(b, proj.Unproject(vertices.back())));
  DistStats stats;
  if (a == b) {
    EXPECT_EQ(1, vertices.size());
    return stats;
  }
  R2Point px = vertices[0];
  S2Point x = proj.Unproject(px);
  for (int i = 1; i < vertices.size(); ++i) {
    R2Point py = vertices[i];
    S2Point y = proj.Unproject(py);
    // Check that every vertex is on the geodesic edge.
    static S1ChordAngle kMaxInterpolationError(S1Angle::Radians(1e-14));
    EXPECT_TRUE(S2::IsDistanceLess(y, a, b, kMaxInterpolationError));
    stats.Tally(GetMaxDistance(proj, px, x, py, y));
    x = y;
    px = py;
  }
  S2_LOG(INFO) << vertices.size() << " vertices, min/avg/max tolerance ratio = "
            << (stats.min_dist() / tolerance) << " / "
            << (stats.avg_dist() / tolerance) << " / "
            << (stats.max_dist() / tolerance);
  return stats;
}

TEST(S2EdgeTessellator, ProjectedNoTessellation) {
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<R2Point> vertices;
  tess.AppendProjected(S2Point(1, 0, 0), S2Point(0, 1, 0), &vertices);
  EXPECT_EQ(2, vertices.size());
}

TEST(S2EdgeTessellator, UnprojectedNoTessellation) {
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  tess.AppendUnprojected(R2Point(0, 30), R2Point(0, 50), &vertices);
  EXPECT_EQ(2, vertices.size());
}

TEST(S2EdgeTessellator, UnprojectedWrapping) {
  // This tests that a projected edge that crosses the 180 degree meridian
  // goes the "short way" around the sphere.

  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  tess.AppendUnprojected(R2Point(-170, 0), R2Point(170, 80), &vertices);
  for (const auto& v : vertices) {
    EXPECT_GE(fabs(S2LatLng::Longitude(v).degrees()), 170);
  }
}

TEST(S2EdgeTessellator, ProjectedWrapping) {
  // This tests projecting a geodesic edge that crosses the 180 degree
  // meridian.  This results in a set of vertices that may be non-canonical
  // (i.e., absolute longitudes greater than 180 degrees) but that don't have
  // any sudden jumps in value, which is convenient for interpolating them.
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<R2Point> vertices;
  tess.AppendProjected(S2LatLng::FromDegrees(0, -170).ToPoint(),
                       S2LatLng::FromDegrees(0, 170).ToPoint(), &vertices);
  for (const auto& v : vertices) {
    EXPECT_LE(v.x(), -170);
  }
}

TEST(S2EdgeTessellator, UnprojectedWrappingMultipleCrossings) {
  // Tests an edge chain that crosses the 180 degree meridian multiple times.
  // Note that due to coordinate wrapping, the last vertex of one edge may not
  // exactly match the first edge of the next edge after unprojection.
  S2::PlateCarreeProjection proj(180);
  S2EdgeTessellator tess(&proj, S1Angle::Degrees(0.01));
  vector<S2Point> vertices;
  for (double lat = 1; lat <= 60; ++lat) {
    tess.AppendUnprojected(R2Point(180 - 0.03 * lat, lat),
                           R2Point(-180 + 0.07 * lat, lat), &vertices);
    tess.AppendUnprojected(R2Point(-180 + 0.07 * lat, lat),
                           R2Point(180 - 0.03 * (lat + 1), lat + 1), &vertices);
  }
  for (const auto& v : vertices) {
    EXPECT_GE(fabs(S2LatLng::Longitude(v).degrees()), 175);
  }
}

TEST(S2EdgeTessellator, ProjectedWrappingMultipleCrossings) {
  // The following loop crosses the 180 degree meridian four times (twice in
  // each direction).
  auto loop = ParsePointsOrDie("0:160, 0:-40, 0:120, 0:-80, 10:120, "
                               "10:-40, 0:160");
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance(S1Angle::E7(1));
  S2EdgeTessellator tess(&proj, tolerance);
  vector<R2Point> vertices;
  for (int i = 0; i + 1 < loop.size(); ++i) {
    tess.AppendProjected(loop[i], loop[i + 1], &vertices);
  }
  EXPECT_EQ(vertices.front(), vertices.back());

  // Note that the R2Point coordinates are in (lng, lat) order.
  double min_lng = vertices[0].x();
  double max_lng = vertices[0].x();
  for (const R2Point& v : vertices) {
    min_lng = min(min_lng, v.x());
    max_lng = max(max_lng, v.x());
  }
  EXPECT_EQ(160, min_lng);
  EXPECT_EQ(640, max_lng);
}

TEST(S2EdgeTessellator, UnprojectedAccuracy) {
  S2::MercatorProjection proj(180);
  S1Angle tolerance(S1Angle::Degrees(1e-5));
  R2Point pa(0, 0), pb(89.999999, 179);
  DistStats stats = TestUnprojected(proj, tolerance, pa, pb);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, DISABLED_UnprojectedAccuracyCrossEquator) {
  S2::MercatorProjection proj(180);
  S1Angle tolerance(S1Angle::Degrees(1e-5));
  R2Point pa(-10, -10), pb(10, 10);
  DistStats stats = TestUnprojected(proj, tolerance, pa, pb);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, ProjectedAccuracy) {
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance(S1Angle::E7(1));
  S2Point a = S2LatLng::FromDegrees(-89.999, -170).ToPoint();
  S2Point b = S2LatLng::FromDegrees(50, 100).ToPoint();
  DistStats stats = TestProjected(proj, tolerance, a, b);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, DISABLED_ProjectedAccuracyCrossEquator) {
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance(S1Angle::E7(1));
  S2Point a = S2LatLng::FromDegrees(-20, -20).ToPoint();
  S2Point b = S2LatLng::FromDegrees(20, 20).ToPoint();
  DistStats stats = TestProjected(proj, tolerance, a, b);
  EXPECT_LT(stats.max_dist(), tolerance);
}

TEST(S2EdgeTessellator, ProjectedAccuracySeattleToNewYork) {
  S2::PlateCarreeProjection proj(180);
  S1Angle tolerance = S2Testing::MetersToAngle(1.23);
  S2Point seattle(S2LatLng::FromDegrees(47.6062, -122.3321).ToPoint());
  S2Point newyork(S2LatLng::FromDegrees(40.7128, -74.0059).ToPoint());
  DistStats stats = TestProjected(proj, tolerance, seattle, newyork);
  EXPECT_LT(stats.max_dist(), tolerance);
}

}  // namespace
<|endoftext|>


<commit_before>#include "command.hh"
#include "shared.hh"
#include "eval.hh"
#include "attr-path.hh"
#include "progress-bar.hh"

#include <unistd.h>

using namespace nix;

struct CmdEdit : InstallableCommand
{
    std::string name() override
    {
        return "edit";
    }

    std::string description() override
    {
        return "open the Nix expression of a Nix package in $EDITOR";
    }

    Examples examples() override
    {
        return {
            Example{
                "To open the Nix expression of the GNU Hello package:",
                "nix edit nixpkgs.hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        auto state = getEvalState();

        auto v = installable->toValue(*state);

        Value * v2;
        try {
            auto dummyArgs = state->allocBindings(0);
            v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v);
        } catch (Error &) {
            throw Error("package '%s' has no source location information", installable->what());
        }

        auto pos = state->forceString(*v2);
        debug("position is %s", pos);

        auto colon = pos.rfind(':');
        if (colon == std::string::npos)
            throw Error("cannot parse meta.position attribute '%s'", pos);

        std::string filename(pos, 0, colon);
        int lineno = std::stoi(std::string(pos, colon + 1));

        auto editor = getEnv("EDITOR", "cat");

        Strings args{editor};

        if (editor.find("emacs") != std::string::npos ||
            editor.find("nano") != std::string::npos ||
            editor.find("vim") != std::string::npos)
            args.push_back(fmt("+%d", lineno));

        args.push_back(filename);

        stopProgressBar();

        execvp(editor.c_str(), stringsToCharPtrs(args).data());

        throw SysError("cannot run editor '%s'", editor);
    }
};

static RegisterCommand r1(make_ref<CmdEdit>());
<commit_msg>edit: Catch stoi exceptions from line number parsing<commit_after>#include "command.hh"
#include "shared.hh"
#include "eval.hh"
#include "attr-path.hh"
#include "progress-bar.hh"

#include <unistd.h>

using namespace nix;

struct CmdEdit : InstallableCommand
{
    std::string name() override
    {
        return "edit";
    }

    std::string description() override
    {
        return "open the Nix expression of a Nix package in $EDITOR";
    }

    Examples examples() override
    {
        return {
            Example{
                "To open the Nix expression of the GNU Hello package:",
                "nix edit nixpkgs.hello"
            },
        };
    }

    void run(ref<Store> store) override
    {
        auto state = getEvalState();

        auto v = installable->toValue(*state);

        Value * v2;
        try {
            auto dummyArgs = state->allocBindings(0);
            v2 = findAlongAttrPath(*state, "meta.position", *dummyArgs, *v);
        } catch (Error &) {
            throw Error("package '%s' has no source location information", installable->what());
        }

        auto pos = state->forceString(*v2);
        debug("position is %s", pos);

        auto colon = pos.rfind(':');
        if (colon == std::string::npos)
            throw Error("cannot parse meta.position attribute '%s'", pos);

        std::string filename(pos, 0, colon);
        int lineno;
        try {
            lineno = std::stoi(std::string(pos, colon + 1));
        } catch (std::invalid_argument e) {
            throw Error("cannot parse line number '%s'", pos);
        }

        auto editor = getEnv("EDITOR", "cat");

        Strings args{editor};

        if (editor.find("emacs") != std::string::npos ||
            editor.find("nano") != std::string::npos ||
            editor.find("vim") != std::string::npos)
            args.push_back(fmt("+%d", lineno));

        args.push_back(filename);

        stopProgressBar();

        execvp(editor.c_str(), stringsToCharPtrs(args).data());

        throw SysError("cannot run editor '%s'", editor);
    }
};

static RegisterCommand r1(make_ref<CmdEdit>());
<|endoftext|>


<commit_before>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  using typeid_t = void (*)();

  template <typename T>
  static typeid_t type_id()
  {
    return typeid_t(type_id<T>);
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->clone() : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>::value
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename ::std::remove_reference<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() noexcept { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs) { return *this = any(rhs); }

  any& operator=(any&& rhs) noexcept
  {
    content = rhs.content;
    rhs.content = nullptr;

    return *this;
  }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type() const noexcept
  {
    return content ? content->type() : type_id<void>();
  }

private: // types

  struct placeholder
  {
    placeholder() = default;

    virtual ~placeholder() = default;

    virtual placeholder* clone() const = 0;

    virtual typeid_t type() const = 0;
  };

  template <typename ValueType, typename = void>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T> holder(T&& value) :
      held(::std::forward<T>(value))
    {
    }

    holder& operator=(holder const&) = delete;

    placeholder* clone() const final { throw ::std::logic_error(""); }

  public: // queries
    typeid_t type() const noexcept final
    {
      return type_id<ValueType>();
    }

  public:
    ValueType held;
  };

  template <typename ValueType>
  struct holder<
    ValueType,
    typename ::std::enable_if<
      ::std::is_copy_constructible<ValueType>::value
    >::type
  > : public placeholder
  {
  public: // constructor
    template <class T>
    holder(T&& value) :
      held(::std::forward<T>(value))
    {
    }

    placeholder* clone() const final { return new holder<ValueType>(held); }

  public: // queries
    typeid_t type() const noexcept final
    {
      return type_id<ValueType>();
    }

  public:
    ValueType held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type() ==
      any::type_id<typename ::std::decay<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (!result)
  {
    throw ::std::bad_cast();
  }
  // else do nothing

  return *result;
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

}

#endif // ANY_HPP
<commit_msg>revert<commit_after>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  using typeid_t = void (*)();

  template <typename T>
  static typeid_t type_id()
  {
    return typeid_t(type_id<T>);
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->clone() : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>::value
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename ::std::decay<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() noexcept { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs) { return *this = any(rhs); }

  any& operator=(any&& rhs) noexcept
  {
    content = rhs.content;
    rhs.content = nullptr;

    return *this;
  }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type() const noexcept
  {
    return content ? content->type() : type_id<void>();
  }

private: // types

  struct placeholder
  {
    placeholder() = default;

    virtual ~placeholder() = default;

    virtual placeholder* clone() const = 0;

    virtual typeid_t type() const = 0;
  };

  template <typename ValueType, typename = void>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T> holder(T&& value) :
      held(::std::forward<T>(value))
    {
    }

    holder& operator=(holder const&) = delete;

    placeholder* clone() const final { throw ::std::logic_error(""); }

  public: // queries
    typeid_t type() const noexcept final
    {
      return type_id<ValueType>();
    }

  public:
    ValueType held;
  };

  template <typename ValueType>
  struct holder<
    ValueType,
    typename ::std::enable_if<
      ::std::is_copy_constructible<ValueType>::value
    >::type
  > : public placeholder
  {
  public: // constructor
    template <class T>
    holder(T&& value) :
      held(::std::forward<T>(value))
    {
    }

    placeholder* clone() const final { return new holder<ValueType>(held); }

  public: // queries
    typeid_t type() const noexcept final
    {
      return type_id<ValueType>();
    }

  public:
    ValueType held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type() ==
      any::type_id<typename ::std::decay<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (!result)
  {
    throw ::std::bad_cast();
  }
  // else do nothing

  return *result;
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

}

#endif // ANY_HPP
<|endoftext|>


<commit_before>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  template <typename T>
  using remove_cvr = ::std::remove_cv<
    typename ::std::remove_reference<T>::type
  >;

  using typeid_t = void const*;

  template <typename T>
  static typeid_t type_id() noexcept
  {
    static char const type_id{};

    return &type_id;
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->cloner_(other.content) : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>{}
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename remove_cvr<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs) { return *this = any(rhs); }

  any& operator=(any&& rhs) noexcept { swap(rhs); return *this; }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename remove_cvr<ValueType>::type>{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type() const noexcept { return type_id(); }

  typeid_t type_id() const noexcept
  {
    return content ? content->type_id_ : type_id<void>();
  }

public: // get

  template <typename ValueType>
  ValueType cget() const
  {
    return get<ValueType>();
  }

  template <typename ValueType>
  ValueType get() const
  {
    using nonref = typename ::std::remove_reference<ValueType>::type;

    return const_cast<any*>(this)->get<nonref const&>();
  }

  template <typename ValueType>
  ValueType get()
  {
    using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
    if (content && (type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()))
    {
      return static_cast<any::holder<nonref>*>(content)->held;
    }
    else
    {
      throw ::std::bad_typeid();
    }
#else
    return static_cast<any::holder<nonref>*>(content)->held;
#endif // NDEBUG
  }

private: // types

  template <typename T>
  static constexpr T* begin(T& value) noexcept
  {
    return &value;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  begin(T (&array)[N]) noexcept
  {
    return begin(array[0]);
  }

  template <typename T>
  static constexpr T* end(T& value) noexcept
  {
    return &value + 1;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  end(T (&array)[N]) noexcept
  {
    return end(array[N - 1]);
  }

  struct placeholder
  {
    typeid_t const type_id_;

    placeholder* (* const cloner_)(placeholder*);

    virtual ~placeholder() = default;

  protected:

    placeholder(typeid_t const ti, decltype(cloner_) const c) noexcept :
      type_id_(ti),
      cloner_(c)
    {
    }
  };

  template <typename ValueType>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        !::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        ::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        !::std::is_copy_assignable<
          typename ::std::remove_all_extents<U>::type
        >{} &&
        ::std::is_move_assignable<
          typename ::std::remove_all_extents<U>::type
        >{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner)
    {
      ::std::copy(::std::make_move_iterator(begin(value)),
        ::std::make_move_iterator(end(value)),
        begin(held));
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_copy_assignable<
          typename ::std::remove_all_extents<U>::type
        >{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner)
    {
      ::std::copy(begin(value), end(value), begin(held));
    }

    holder& operator=(holder const&) = delete;

    static placeholder* cloner(placeholder* const base)
    {
      return new holder<ValueType>(static_cast<holder*>(base)->held);
    }

    static placeholder* throwing_cloner(placeholder* const)
    {
      throw ::std::logic_error("");
    }

  public:
    ValueType held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (result)
  {
    return *result;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

}

#endif // ANY_HPP
<commit_msg>some fixes<commit_after>#ifndef ANY_HPP
# define ANY_HPP
# pragma once

#include <cassert>

#include <cstdint>

#include <stdexcept>

#include <typeinfo>

#include <type_traits>

#include <utility>

namespace generic
{

class any
{
public:
  template <typename T>
  using remove_cvr = ::std::remove_cv<
    typename ::std::remove_reference<T>::type
  >;

  using typeid_t = void const*;

  template <typename T>
  static typeid_t type_id() noexcept
  {
    static char const type_id{};

    return &type_id;
  }

  any() = default;

  any(any const& other) :
    content(other.content ? other.content->cloner_(other.content) : nullptr)
  {
  }

  any(any&& other) noexcept { *this = ::std::move(other); }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename ::std::decay<ValueType>::type>{}
    >::type
  >
  any(ValueType&& value) :
    content(new holder<typename remove_cvr<ValueType>::type>(
      ::std::forward<ValueType>(value)))
  {
  }

  ~any() { delete content; }

public: // modifiers
  void clear() { swap(any()); }

  bool empty() const noexcept { return !*this; }

  void swap(any& other) noexcept { ::std::swap(content, other.content); }

  void swap(any&& other) noexcept { ::std::swap(content, other.content); }

  any& operator=(any const& rhs) { return *this = any(rhs); }

  any& operator=(any&& rhs) noexcept { swap(rhs); return *this; }

  template<typename ValueType,
    typename = typename ::std::enable_if<
      !::std::is_same<any, typename remove_cvr<ValueType>::type>{}
    >::type
  >
  any& operator=(ValueType&& rhs)
  {
    return *this = any(::std::forward<ValueType>(rhs));
  }

public: // queries

  explicit operator bool() const noexcept { return content; }

  typeid_t type() const noexcept { return type_id(); }

  typeid_t type_id() const noexcept
  {
    return content ? content->type_id_ : type_id<void>();
  }

public: // get

  template <typename ValueType>
  ValueType cget() const
  {
    return get<ValueType>();
  }

  template <typename ValueType>
  ValueType get() const
  {
    using nonref = typename ::std::remove_reference<ValueType>::type;

    return const_cast<any*>(this)->get<nonref const&>();
  }

  template <typename ValueType>
  ValueType get()
  {
    using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
    if (content && (type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()))
    {
      return static_cast<any::holder<nonref>*>(content)->held;
    }
    else
    {
      throw ::std::bad_typeid();
    }
#else
    return static_cast<any::holder<nonref>*>(content)->held;
#endif // NDEBUG
  }

private: // types

  template <typename T>
  static constexpr T* begin(T& value) noexcept
  {
    return &value;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  begin(T (&array)[N]) noexcept
  {
    return begin(*array);
  }

  template <typename T>
  static constexpr T* end(T& value) noexcept
  {
    return &value + 1;
  }

  template <typename T, ::std::size_t N>
  static constexpr typename ::std::remove_all_extents<T>::type*
  end(T (&array)[N]) noexcept
  {
    return end(array[N - 1]);
  }

  struct placeholder
  {
    typeid_t const type_id_;

    placeholder* (* const cloner_)(placeholder*);

    virtual ~placeholder() = default;

  protected:

    placeholder(typeid_t const ti, decltype(cloner_) const c) noexcept :
      type_id_(ti),
      cloner_(c)
    {
    }
  };

  template <typename ValueType>
  struct holder : public placeholder
  {
  public: // constructor
    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        !::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        !::std::is_array<U>{} &&
        ::std::is_copy_constructible<U>{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner),
      held(::std::forward<T>(value))
    {
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        !::std::is_copy_assignable<
          typename ::std::remove_all_extents<U>::type
        >{} &&
        ::std::is_move_assignable<
          typename ::std::remove_all_extents<U>::type
        >{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), throwing_cloner)
    {
      ::std::copy(::std::make_move_iterator(begin(value)),
        ::std::make_move_iterator(end(value)),
        begin(held));
    }

    template <class T, typename U = ValueType>
    holder(T&& value,
      typename ::std::enable_if<
        ::std::is_array<U>{} &&
        ::std::is_copy_assignable<
          typename ::std::remove_all_extents<U>::type
        >{}
      >::type* = nullptr) :
      placeholder(type_id<ValueType>(), cloner)
    {
      ::std::copy(begin(value), end(value), begin(held));
    }

    holder& operator=(holder const&) = delete;

    static placeholder* cloner(placeholder* const base)
    {
      return new holder<ValueType>(static_cast<holder*>(base)->held);
    }

    static placeholder* throwing_cloner(placeholder* const)
    {
      throw ::std::logic_error("");
    }

  public:
    ValueType held;
  };

private: // representation

  template<typename ValueType>
  friend ValueType* any_cast(any*) noexcept;

  template<typename ValueType>
  friend ValueType* unsafe_any_cast(any*) noexcept;

  placeholder* content{};
};

template<typename ValueType>
inline ValueType* unsafe_any_cast(any* const operand) noexcept
{
  return &static_cast<any::holder<ValueType>*>(operand->content)->held;
}

template<typename ValueType>
inline ValueType const* unsafe_any_cast(any const* const operand) noexcept
{
  return unsafe_any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType* any_cast(any* const operand) noexcept
{
  return operand &&
    (operand->type_id() ==
      any::type_id<typename any::remove_cvr<ValueType>::type>()) ?
    &static_cast<any::holder<ValueType>*>(operand->content)->held :
    nullptr;
}

template<typename ValueType>
inline ValueType const* any_cast(any const* const operand) noexcept
{
  return any_cast<ValueType>(const_cast<any*>(operand));
}

template<typename ValueType>
inline ValueType any_cast(any& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

#ifndef NDEBUG
  auto const result(any_cast<nonref>(&operand));

  if (result)
  {
    return *result;
  }
  else
  {
    throw ::std::bad_cast();
  }
#else
  return *unsafe_any_cast<nonref>(&operand);
#endif // NDEBUG
}

template<typename ValueType>
inline ValueType any_cast(any const& operand)
{
  using nonref = typename ::std::remove_reference<ValueType>::type;

  return any_cast<nonref const&>(const_cast<any&>(operand));
}

}

#endif // ANY_HPP
<|endoftext|>