<commit_before>// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// 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.

#ifndef RCLCPP__SUBSCRIPTION_BASE_HPP_
#define RCLCPP__SUBSCRIPTION_BASE_HPP_

#include <atomic>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>

#include "rcl/subscription.h"

#include "rmw/rmw.h"

#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{

namespace node_interfaces
{
class NodeBaseInterface;
}  // namespace node_interfaces

namespace experimental
{
/**
 * IntraProcessManager is forward declared here, avoiding a circular inclusion between
 * `intra_process_manager.hpp` and `subscription_base.hpp`.
 */
class IntraProcessManager;
}  // namespace experimental

/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)

  /// Constructor.
  /**
   * This accepts rcl_subscription_options_t instead of rclcpp::SubscriptionOptions because
   * rclcpp::SubscriptionOptions::to_rcl_subscription_options depends on the message type.
   *
   * \param[in] node_base NodeBaseInterface pointer used in parts of the setup.
   * \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
   * \param[in] topic_name Name of the topic to subscribe to.
   * \param[in] subscription_options Options for the subscription.
   * \param[in] is_serialized is true if the message will be delivered still serialized
   */
  RCLCPP_PUBLIC
  SubscriptionBase(
    rclcpp::node_interfaces::NodeBaseInterface * node_base,
    const rosidl_message_type_support_t & type_support_handle,
    const std::string & topic_name,
    const rcl_subscription_options_t & subscription_options,
    bool is_serialized = false);

  /// Destructor.
  RCLCPP_PUBLIC
  virtual ~SubscriptionBase();

  /// Get the topic that this subscription is subscribed on.
  RCLCPP_PUBLIC
  const char *
  get_topic_name() const;

  RCLCPP_PUBLIC
  std::shared_ptr<rcl_subscription_t>
  get_subscription_handle();

  RCLCPP_PUBLIC
  std::shared_ptr<const rcl_subscription_t>
  get_subscription_handle() const;

  /// Get all the QoS event handlers associated with this subscription.
  /** \return The vector of QoS event handlers. */
  RCLCPP_PUBLIC
  const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
  get_event_handlers() const;

  /// Get the actual QoS settings, after the defaults have been determined.
  /**
   * The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
   * can only be resolved after the creation of the publisher, and it
   * depends on the underlying rmw implementation.
   * If the underlying setting in use can't be represented in ROS terms,
   * it will be set to RMW_QOS_POLICY_*_UNKNOWN.
   * May throw runtime_error when an unexpected error occurs.
   *
   * \return The actual qos settings.
   * \throws std::runtime_error if failed to get qos settings
   */
  RCLCPP_PUBLIC
  rclcpp::QoS
  get_actual_qos() const;

  /// Take the next inter-process message from the subscription as a type erased pointer.
  /**
   * \sa Subscription::take() for details on how this function works.
   *
   * The only difference is that it takes a type erased pointer rather than a
   * reference to the exact message type.
   *
   * This type erased version facilitates using the subscriptions in a type
   * agnostic way using SubscriptionBase::create_message() and
   * SubscriptionBase::handle_message().
   *
   * \param[out] message_out The type erased message pointer into which take
   *   will copy the data.
   * \param[out] message_info_out The message info for the taken message.
   * \returns true if data was taken and is valid, otherwise false
   * \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
   */
  RCLCPP_PUBLIC
  bool
  take_type_erased(void * message_out, rclcpp::MessageInfo & message_info_out);

  /// Take the next inter-process message, in its serialized form, from the subscription.
  /**
   * For now, if data is taken (written) into the message_out and
   * message_info_out then true will be returned.
   * Unlike Subscription::take(), taking data serialized is not possible via
   * intra-process for the time being, so it will not need to de-duplicate
   * data in any case.
   *
   * \param[out] message_out The serialized message data structure used to
   *   store the taken message.
   * \param[out] message_info_out The message info for the taken message.
   * \returns true if data was taken and is valid, otherwise false
   * \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
   */
  RCLCPP_PUBLIC
  bool
  take_serialized(rclcpp::SerializedMessage & message_out, rclcpp::MessageInfo & message_info_out);

  /// Borrow a new message.
  /** \return Shared pointer to the fresh message. */
  RCLCPP_PUBLIC
  virtual
  std::shared_ptr<void>
  create_message() = 0;

  /// Borrow a new serialized message
  /** \return Shared pointer to a rcl_message_serialized_t. */
  RCLCPP_PUBLIC
  virtual
  std::shared_ptr<rclcpp::SerializedMessage>
  create_serialized_message() = 0;

  /// Check if we need to handle the message, and execute the callback if we do.
  /**
   * \param[in] message Shared pointer to the message to handle.
   * \param[in] message_info Metadata associated with this message.
   */
  RCLCPP_PUBLIC
  virtual
  void
  handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;

  RCLCPP_PUBLIC
  virtual
  void
  handle_serialized_message(
    const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
    const rclcpp::MessageInfo & message_info) = 0;

  RCLCPP_PUBLIC
  virtual
  void
  handle_loaned_message(void * loaned_message, const rclcpp::MessageInfo & message_info) = 0;

  /// Return the message borrowed in create_message.
  /** \param[in] message Shared pointer to the returned message. */
  RCLCPP_PUBLIC
  virtual
  void
  return_message(std::shared_ptr<void> & message) = 0;

  /// Return the message borrowed in create_serialized_message.
  /** \param[in] message Shared pointer to the returned message. */
  RCLCPP_PUBLIC
  virtual
  void
  return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) = 0;

  RCLCPP_PUBLIC
  const rosidl_message_type_support_t &
  get_message_type_support_handle() const;

  /// Return if the subscription is serialized
  /**
   * \return `true` if the subscription is serialized, `false` otherwise
   */
  RCLCPP_PUBLIC
  bool
  is_serialized() const;

  /// Get matching publisher count.
  /** \return The number of publishers on this topic. */
  RCLCPP_PUBLIC
  size_t
  get_publisher_count() const;

  /// Check if subscription instance can loan messages.
  /**
   * Depending on the middleware and the message type, this will return true if the middleware
   * can allocate a ROS message instance.
   *
   * \return boolean flag indicating if middleware can loan messages.
   */
  RCLCPP_PUBLIC
  bool
  can_loan_messages() const;

  using IntraProcessManagerWeakPtr =
    std::weak_ptr<rclcpp::experimental::IntraProcessManager>;

  /// Implemenation detail.
  RCLCPP_PUBLIC
  void
  setup_intra_process(
    uint64_t intra_process_subscription_id,
    IntraProcessManagerWeakPtr weak_ipm);

  /// Return the waitable for intra-process
  /**
   * \return the waitable sharedpointer for intra-process, or nullptr if intra-process is not setup.
   * \throws std::runtime_error if the intra process manager is destroyed
   */
  RCLCPP_PUBLIC
  rclcpp::Waitable::SharedPtr
  get_intra_process_waitable() const;

  /// Exchange state of whether or not a part of the subscription is used by a wait set.
  /**
   * Used to ensure parts of the subscription are not used with multiple wait
   * sets simultaneously.
   *
   * \param[in] pointer_to_subscription_part address of a subscription part
   * \param[in] in_use_state the new state to exchange, true means "now in use",
   *   and false means "no longer in use".
   * \returns the current "in use" state.
   * \throws std::invalid_argument If pointer_to_subscription_part is nullptr.
   * \throws std::runtime_error If the pointer given is not a pointer to one of
   *   the parts of the subscription which can be used with a wait set.
   */
  RCLCPP_PUBLIC
  bool
  exchange_in_use_by_wait_set_state(void * pointer_to_subscription_part, bool in_use_state);

  /// Get network flow endpoints
  /**
   * Describes network flow endpoints that this subscription is receiving messages on
   * \return vector of NetworkFlowEndpoint
   */
  RCLCPP_PUBLIC
  std::vector<rclcpp::NetworkFlowEndpoint>
  get_network_flow_endpoints() const;

protected:
  template<typename EventCallbackT>
  void
  add_event_handler(
    const EventCallbackT & callback,
    const rcl_subscription_event_type_t event_type)
  {
    auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
        std::shared_ptr<rcl_subscription_t>>>(
      callback,
      rcl_subscription_event_init,
      get_subscription_handle(),
      event_type);
    qos_events_in_use_by_wait_set_.insert(std::make_pair(handler.get(), false));
    event_handlers_.emplace_back(handler);
  }

  RCLCPP_PUBLIC
  void default_incompatible_qos_callback(QOSRequestedIncompatibleQoSInfo & info) const;

  RCLCPP_PUBLIC
  bool
  matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;

  rclcpp::node_interfaces::NodeBaseInterface * const node_base_;

  std::shared_ptr<rcl_node_t> node_handle_;
  std::shared_ptr<rcl_subscription_t> subscription_handle_;
  std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;

  std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;

  bool use_intra_process_;
  IntraProcessManagerWeakPtr weak_ipm_;
  uint64_t intra_process_subscription_id_;

private:
  RCLCPP_DISABLE_COPY(SubscriptionBase)

  rosidl_message_type_support_t type_support_;
  bool is_serialized_;

  std::atomic<bool> subscription_in_use_by_wait_set_{false};
  std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
  std::unordered_map<rclcpp::QOSEventHandlerBase *,
    std::atomic<bool>> qos_events_in_use_by_wait_set_;
};

}  // namespace rclcpp

#endif  // RCLCPP__SUBSCRIPTION_BASE_HPP_
<commit_msg>typo fix. (#1790)<commit_after>// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// 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.

#ifndef RCLCPP__SUBSCRIPTION_BASE_HPP_
#define RCLCPP__SUBSCRIPTION_BASE_HPP_

#include <atomic>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>

#include "rcl/subscription.h"

#include "rmw/rmw.h"

#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"

namespace rclcpp
{

namespace node_interfaces
{
class NodeBaseInterface;
}  // namespace node_interfaces

namespace experimental
{
/**
 * IntraProcessManager is forward declared here, avoiding a circular inclusion between
 * `intra_process_manager.hpp` and `subscription_base.hpp`.
 */
class IntraProcessManager;
}  // namespace experimental

/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
{
public:
  RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)

  /// Constructor.
  /**
   * This accepts rcl_subscription_options_t instead of rclcpp::SubscriptionOptions because
   * rclcpp::SubscriptionOptions::to_rcl_subscription_options depends on the message type.
   *
   * \param[in] node_base NodeBaseInterface pointer used in parts of the setup.
   * \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
   * \param[in] topic_name Name of the topic to subscribe to.
   * \param[in] subscription_options Options for the subscription.
   * \param[in] is_serialized is true if the message will be delivered still serialized
   */
  RCLCPP_PUBLIC
  SubscriptionBase(
    rclcpp::node_interfaces::NodeBaseInterface * node_base,
    const rosidl_message_type_support_t & type_support_handle,
    const std::string & topic_name,
    const rcl_subscription_options_t & subscription_options,
    bool is_serialized = false);

  /// Destructor.
  RCLCPP_PUBLIC
  virtual ~SubscriptionBase();

  /// Get the topic that this subscription is subscribed on.
  RCLCPP_PUBLIC
  const char *
  get_topic_name() const;

  RCLCPP_PUBLIC
  std::shared_ptr<rcl_subscription_t>
  get_subscription_handle();

  RCLCPP_PUBLIC
  std::shared_ptr<const rcl_subscription_t>
  get_subscription_handle() const;

  /// Get all the QoS event handlers associated with this subscription.
  /** \return The vector of QoS event handlers. */
  RCLCPP_PUBLIC
  const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
  get_event_handlers() const;

  /// Get the actual QoS settings, after the defaults have been determined.
  /**
   * The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
   * can only be resolved after the creation of the subscription, and it
   * depends on the underlying rmw implementation.
   * If the underlying setting in use can't be represented in ROS terms,
   * it will be set to RMW_QOS_POLICY_*_UNKNOWN.
   * May throw runtime_error when an unexpected error occurs.
   *
   * \return The actual qos settings.
   * \throws std::runtime_error if failed to get qos settings
   */
  RCLCPP_PUBLIC
  rclcpp::QoS
  get_actual_qos() const;

  /// Take the next inter-process message from the subscription as a type erased pointer.
  /**
   * \sa Subscription::take() for details on how this function works.
   *
   * The only difference is that it takes a type erased pointer rather than a
   * reference to the exact message type.
   *
   * This type erased version facilitates using the subscriptions in a type
   * agnostic way using SubscriptionBase::create_message() and
   * SubscriptionBase::handle_message().
   *
   * \param[out] message_out The type erased message pointer into which take
   *   will copy the data.
   * \param[out] message_info_out The message info for the taken message.
   * \returns true if data was taken and is valid, otherwise false
   * \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
   */
  RCLCPP_PUBLIC
  bool
  take_type_erased(void * message_out, rclcpp::MessageInfo & message_info_out);

  /// Take the next inter-process message, in its serialized form, from the subscription.
  /**
   * For now, if data is taken (written) into the message_out and
   * message_info_out then true will be returned.
   * Unlike Subscription::take(), taking data serialized is not possible via
   * intra-process for the time being, so it will not need to de-duplicate
   * data in any case.
   *
   * \param[out] message_out The serialized message data structure used to
   *   store the taken message.
   * \param[out] message_info_out The message info for the taken message.
   * \returns true if data was taken and is valid, otherwise false
   * \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
   */
  RCLCPP_PUBLIC
  bool
  take_serialized(rclcpp::SerializedMessage & message_out, rclcpp::MessageInfo & message_info_out);

  /// Borrow a new message.
  /** \return Shared pointer to the fresh message. */
  RCLCPP_PUBLIC
  virtual
  std::shared_ptr<void>
  create_message() = 0;

  /// Borrow a new serialized message
  /** \return Shared pointer to a rcl_message_serialized_t. */
  RCLCPP_PUBLIC
  virtual
  std::shared_ptr<rclcpp::SerializedMessage>
  create_serialized_message() = 0;

  /// Check if we need to handle the message, and execute the callback if we do.
  /**
   * \param[in] message Shared pointer to the message to handle.
   * \param[in] message_info Metadata associated with this message.
   */
  RCLCPP_PUBLIC
  virtual
  void
  handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;

  RCLCPP_PUBLIC
  virtual
  void
  handle_serialized_message(
    const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
    const rclcpp::MessageInfo & message_info) = 0;

  RCLCPP_PUBLIC
  virtual
  void
  handle_loaned_message(void * loaned_message, const rclcpp::MessageInfo & message_info) = 0;

  /// Return the message borrowed in create_message.
  /** \param[in] message Shared pointer to the returned message. */
  RCLCPP_PUBLIC
  virtual
  void
  return_message(std::shared_ptr<void> & message) = 0;

  /// Return the message borrowed in create_serialized_message.
  /** \param[in] message Shared pointer to the returned message. */
  RCLCPP_PUBLIC
  virtual
  void
  return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) = 0;

  RCLCPP_PUBLIC
  const rosidl_message_type_support_t &
  get_message_type_support_handle() const;

  /// Return if the subscription is serialized
  /**
   * \return `true` if the subscription is serialized, `false` otherwise
   */
  RCLCPP_PUBLIC
  bool
  is_serialized() const;

  /// Get matching publisher count.
  /** \return The number of publishers on this topic. */
  RCLCPP_PUBLIC
  size_t
  get_publisher_count() const;

  /// Check if subscription instance can loan messages.
  /**
   * Depending on the middleware and the message type, this will return true if the middleware
   * can allocate a ROS message instance.
   *
   * \return boolean flag indicating if middleware can loan messages.
   */
  RCLCPP_PUBLIC
  bool
  can_loan_messages() const;

  using IntraProcessManagerWeakPtr =
    std::weak_ptr<rclcpp::experimental::IntraProcessManager>;

  /// Implemenation detail.
  RCLCPP_PUBLIC
  void
  setup_intra_process(
    uint64_t intra_process_subscription_id,
    IntraProcessManagerWeakPtr weak_ipm);

  /// Return the waitable for intra-process
  /**
   * \return the waitable sharedpointer for intra-process, or nullptr if intra-process is not setup.
   * \throws std::runtime_error if the intra process manager is destroyed
   */
  RCLCPP_PUBLIC
  rclcpp::Waitable::SharedPtr
  get_intra_process_waitable() const;

  /// Exchange state of whether or not a part of the subscription is used by a wait set.
  /**
   * Used to ensure parts of the subscription are not used with multiple wait
   * sets simultaneously.
   *
   * \param[in] pointer_to_subscription_part address of a subscription part
   * \param[in] in_use_state the new state to exchange, true means "now in use",
   *   and false means "no longer in use".
   * \returns the current "in use" state.
   * \throws std::invalid_argument If pointer_to_subscription_part is nullptr.
   * \throws std::runtime_error If the pointer given is not a pointer to one of
   *   the parts of the subscription which can be used with a wait set.
   */
  RCLCPP_PUBLIC
  bool
  exchange_in_use_by_wait_set_state(void * pointer_to_subscription_part, bool in_use_state);

  /// Get network flow endpoints
  /**
   * Describes network flow endpoints that this subscription is receiving messages on
   * \return vector of NetworkFlowEndpoint
   */
  RCLCPP_PUBLIC
  std::vector<rclcpp::NetworkFlowEndpoint>
  get_network_flow_endpoints() const;

protected:
  template<typename EventCallbackT>
  void
  add_event_handler(
    const EventCallbackT & callback,
    const rcl_subscription_event_type_t event_type)
  {
    auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
        std::shared_ptr<rcl_subscription_t>>>(
      callback,
      rcl_subscription_event_init,
      get_subscription_handle(),
      event_type);
    qos_events_in_use_by_wait_set_.insert(std::make_pair(handler.get(), false));
    event_handlers_.emplace_back(handler);
  }

  RCLCPP_PUBLIC
  void default_incompatible_qos_callback(QOSRequestedIncompatibleQoSInfo & info) const;

  RCLCPP_PUBLIC
  bool
  matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;

  rclcpp::node_interfaces::NodeBaseInterface * const node_base_;

  std::shared_ptr<rcl_node_t> node_handle_;
  std::shared_ptr<rcl_subscription_t> subscription_handle_;
  std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;

  std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;

  bool use_intra_process_;
  IntraProcessManagerWeakPtr weak_ipm_;
  uint64_t intra_process_subscription_id_;

private:
  RCLCPP_DISABLE_COPY(SubscriptionBase)

  rosidl_message_type_support_t type_support_;
  bool is_serialized_;

  std::atomic<bool> subscription_in_use_by_wait_set_{false};
  std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
  std::unordered_map<rclcpp::QOSEventHandlerBase *,
    std::atomic<bool>> qos_events_in_use_by_wait_set_;
};

}  // namespace rclcpp

#endif  // RCLCPP__SUBSCRIPTION_BASE_HPP_
<|endoftext|>


<commit_before>/*! \file */ //Copyright 2011-2016 Tyler Gilbert; All Rights Reserved

/*! \mainpage
 *
 * This is the Stratify Application Library documentation.  If you consider yourself an expert, you can check out the
 * Stratify OS documentation.
 *
 * \section INTRO Introduction
 *
 * Stratify Lib is a library for Stratify OS applications that makes it easy to access hardware
 * and other system functions (you can also use standard \ref POSIX and \ref STDC functions if that
 * is your style).
 *
 * If you are new, it would be highly beneficial to quickly read through the naming conventions:
 * https://github.com/StratifyLabs/StratifyLib#naming-conventions.
 *
 * Here are some useful namespaces to check out:
 *
 * - \ref hal - Hardware Abstraction Layer
 * - \ref calc - Useful software algorithms
 * - \ref draw - Drawing on an LCD
 * - \ref fmt - Common file formats
 * - \ref sys - Access to timers and files and such
 * - \ref var - Variable storage in data and strings
 *
 *
 * Here are a couple of examples to give you an idea about how things work.  These are the objects
 * used in the examples.
 *
 * - hal::Uart - access to UART hardware
 * - hal::Pin - read and write pin values
 * - var::String - string manipulation
 * - sys::Timer - Timing and delays
 *
 * \code
 * #include <stfy/hal.hpp> //this will include all namespace classes and add the namespace i.e. "using namespace hal;"
 * #include <stfy/sys.hpp>
 *
 * int main(int argc, char * argv[]){
 *  Pin p(2, 10);
 *  Pin button(2,9);
 *  p.init(Pin::OUTPUT); //open the port and configure pin 10 as an output
 *  button.init(Pin::INPUT | Pin::PULLUP); //pin 9 is an input (pulled high)
 *
 *  //toggle p until the button is pressed
 *  while( button.value() != 0 ){
 *  	p = false;
 *  	Timer::wait_msec(100);
 *  	p = true;
 *  	Timer::wait_msec(100);
 *  }
 *
 * 	return 0;
 * }
 * \endcode
 *
 *
 * Here is an example of a UART loop back routine.
 *
 *
 * \code
 * #include <stfy/hal.hpp>
 * #include <stfy/var.hpp>
 *
 * int main(int argc, char * argv[]){
 *  Uart uart(0);
 *	int ret;
 *	String exitCommand = "exit\n";
 *	bool done = false;
 *  char buffer[64];
 *  uart.init(115200);
 *
 *  //loopback UART until exit is entered
 *  while( done == false ){
 *  	if( (ret = uart.read(buffer,64)) > 0 ){
 *  		if( exitCommand == buffer ){
 *  			done = true;
 *  		}
 *			uart.write(buffer, ret);
 *  	}
 *  }
 *
 * 	return 0;
 * }
 * \endcode
 *
 * The best way to learn is to get started:  https://github.com/StratifyLabs/HelloWorld#helloworld.
 *
 */

#ifndef STFY_APP_PHY_HPP_
#define STFY_APP_PHY_HPP_

#include <unistd.h>
#include <iface/link.h>
#include <fcntl.h>

#ifndef __link
#include "../sys/Aio.hpp"
#else
#undef fileno
#endif

#include "../var/String.hpp"

namespace hal {

/*! \brief Base Applib Class for physical devices
 * \details This is the base class for physical devices.
 *
 */
class Dev {
public:
	Dev();

	enum {
		RDONLY /*! Open as read-only */ = LINK_O_RDONLY,
		READONLY /*! Open as read-only */ = LINK_O_RDONLY,
		WRONLY /*! Open as write-only */ = LINK_O_WRONLY,
		WRITEONLY /*! Open as write-only */ = LINK_O_WRONLY,
		CREATE /*! Create when opening (files) */ = LINK_O_CREAT,
		CREAT /*! Create when opening (files) */ = LINK_O_CREAT,
		TRUNCATE /*! Truncate when opening (files) */ = LINK_O_TRUNC,
		TRUNC /*! Truncate when opening (files) */ = LINK_O_TRUNC,
		APPEND /*! Append when opening (files)*/ = LINK_O_APPEND,
		EXCLUSIVE /*! Create exclusively (files) */ = LINK_O_EXCL,
		EXCL /*! Create exclusively (files) */ = LINK_O_EXCL,
		READWRITE /*! Open as read-write */ = LINK_O_RDWR,
		RDWR /*! Open as read-write */ = LINK_O_RDWR,
		NONBLOCK /*! Open as non-blocking */ = LINK_O_NONBLOCK,
		NDELAY /*! Open as non-blocking */ = LINK_O_NONBLOCK,
		ACCMODE /*! Access mode mask */ = LINK_O_ACCMODE
	};

	/*! \details List of options for \a whence argument of seek() */
	enum {
		SET = SEEK_SET,
		CURRENT = SEEK_CUR,
		END = SEEK_END

	};

	/*! \details Get the name of the file from a given path */
	static const char * name(const char * path);

	/*! \details Open the specified file or device */
	virtual int open(const char * name, int flags);

	/*! \details IO Ctl requests */
	virtual int ioctl(int req, void * arg) const;

	/*! \details Seek to a location in the file or on the device */
	virtual int seek(int loc, int whence = LINK_SEEK_SET) const;

	/*! \details Returns the location of the cursor in the device or file */
	int loc() const;

	/*! \details Return the current flags for the file */
	int flags() const;

	/*! \details Return the file number for accessing the file or device */
	int fileno() const;

	/*! \details Close the file or device */
	virtual int close();

	/*! \details reads the device. */
	virtual int read(void * buf, int nbyte) const;

	/*! \details writes data to the device */
	virtual int write(const void * buf, int nbyte) const;

#ifndef __link
	/*! \details Asyncronous read */
	virtual int read(sys::Aio & aio) const;
	/*! \details Asyncronous write */
	virtual int write(sys::Aio & aio) const;
#endif

	/*! \details reads the device at the location */
	int read(int loc, void * buf, int nbyte) const;

	/*! \details writes the device at the location
	 *
	 * @param loc Location to read (not application to character devices)
	 * @param buf Pointer to the source data
	 * @param nbyte Number of bytes to write
	 * @return Number of bytes successfully written or -1 with errno set
	 */
	int write(int loc, const void * buf, int nbyte) const;

	/*! \details writes a string to the device */
	int write(const char * s) const { return write(s, strlen(s)); }

	/*! \details Read a line from the device or file */
	int readline(char * buf, int nbyte, int timeout, char term) const;

	/*! \details Write a Var::String to the file
	 *
	 * @param str The string to write
	 * @return The number of bytes written
	 */
	inline int write(const var::String & str) const { return write(str.c_str(), str.size()); }

	enum {
		GETS_BUFFER_SIZE = 128
	};

	/*! \details Read up to n-1 bytes to \a s until end-of-file or \a term is reached.  */
	char * gets(char * s, int n, char term = '\n') const;

	/*! \details Read a line in to the string */
	char * gets(var::String & s, char term = '\n') const { return gets(s.cdata(), s.capacity(), term); }

#ifdef __link
	void set_driver(link_transport_mdriver_t * d){ _driver = d; }
	link_transport_mdriver_t * driver() const { return _driver; }
#endif

	/*! \details ioctl() with request and const arg pointer */
	int ioctl(int req, const void * arg) const { return ioctl(req, (void*)arg); }
	/*! \details ioctl() with just a request */
	int ioctl(int req) const { return ioctl(req, (void*)NULL); }
	/*! \details ioctl() with request and integer arg */
	int ioctl(int req, int arg) const { return ioctl(req, (void*)(size_t)arg); }

protected:
	mutable int fd;

#ifdef __link
	link_transport_mdriver_t * _driver;
#endif

};

};

#endif /* STFY_APP_PHYBASE_HPP_ */
<commit_msg>Link build fix with Windows and unistd<commit_after>/*! \file */ //Copyright 2011-2016 Tyler Gilbert; All Rights Reserved

/*! \mainpage
 *
 * This is the Stratify Application Library documentation.  If you consider yourself an expert, you can check out the
 * Stratify OS documentation.
 *
 * \section INTRO Introduction
 *
 * Stratify Lib is a library for Stratify OS applications that makes it easy to access hardware
 * and other system functions (you can also use standard \ref POSIX and \ref STDC functions if that
 * is your style).
 *
 * If you are new, it would be highly beneficial to quickly read through the naming conventions:
 * https://github.com/StratifyLabs/StratifyLib#naming-conventions.
 *
 * Here are some useful namespaces to check out:
 *
 * - \ref hal - Hardware Abstraction Layer
 * - \ref calc - Useful software algorithms
 * - \ref draw - Drawing on an LCD
 * - \ref fmt - Common file formats
 * - \ref sys - Access to timers and files and such
 * - \ref var - Variable storage in data and strings
 *
 *
 * Here are a couple of examples to give you an idea about how things work.  These are the objects
 * used in the examples.
 *
 * - hal::Uart - access to UART hardware
 * - hal::Pin - read and write pin values
 * - var::String - string manipulation
 * - sys::Timer - Timing and delays
 *
 * \code
 * #include <stfy/hal.hpp> //this will include all namespace classes and add the namespace i.e. "using namespace hal;"
 * #include <stfy/sys.hpp>
 *
 * int main(int argc, char * argv[]){
 *  Pin p(2, 10);
 *  Pin button(2,9);
 *  p.init(Pin::OUTPUT); //open the port and configure pin 10 as an output
 *  button.init(Pin::INPUT | Pin::PULLUP); //pin 9 is an input (pulled high)
 *
 *  //toggle p until the button is pressed
 *  while( button.value() != 0 ){
 *  	p = false;
 *  	Timer::wait_msec(100);
 *  	p = true;
 *  	Timer::wait_msec(100);
 *  }
 *
 * 	return 0;
 * }
 * \endcode
 *
 *
 * Here is an example of a UART loop back routine.
 *
 *
 * \code
 * #include <stfy/hal.hpp>
 * #include <stfy/var.hpp>
 *
 * int main(int argc, char * argv[]){
 *  Uart uart(0);
 *	int ret;
 *	String exitCommand = "exit\n";
 *	bool done = false;
 *  char buffer[64];
 *  uart.init(115200);
 *
 *  //loopback UART until exit is entered
 *  while( done == false ){
 *  	if( (ret = uart.read(buffer,64)) > 0 ){
 *  		if( exitCommand == buffer ){
 *  			done = true;
 *  		}
 *			uart.write(buffer, ret);
 *  	}
 *  }
 *
 * 	return 0;
 * }
 * \endcode
 *
 * The best way to learn is to get started:  https://github.com/StratifyLabs/HelloWorld#helloworld.
 *
 */

#ifndef STFY_APP_PHY_HPP_
#define STFY_APP_PHY_HPP_

#include <iface/link.h>
#include <fcntl.h>

#ifndef __link
#include <unistd.h>
#include "../sys/Aio.hpp"
#else
#undef fileno
#endif

#include "../var/String.hpp"

namespace hal {

/*! \brief Base Applib Class for physical devices
 * \details This is the base class for physical devices.
 *
 */
class Dev {
public:
	Dev();

	enum {
		RDONLY /*! Open as read-only */ = LINK_O_RDONLY,
		READONLY /*! Open as read-only */ = LINK_O_RDONLY,
		WRONLY /*! Open as write-only */ = LINK_O_WRONLY,
		WRITEONLY /*! Open as write-only */ = LINK_O_WRONLY,
		CREATE /*! Create when opening (files) */ = LINK_O_CREAT,
		CREAT /*! Create when opening (files) */ = LINK_O_CREAT,
		TRUNCATE /*! Truncate when opening (files) */ = LINK_O_TRUNC,
		TRUNC /*! Truncate when opening (files) */ = LINK_O_TRUNC,
		APPEND /*! Append when opening (files)*/ = LINK_O_APPEND,
		EXCLUSIVE /*! Create exclusively (files) */ = LINK_O_EXCL,
		EXCL /*! Create exclusively (files) */ = LINK_O_EXCL,
		READWRITE /*! Open as read-write */ = LINK_O_RDWR,
		RDWR /*! Open as read-write */ = LINK_O_RDWR,
		NONBLOCK /*! Open as non-blocking */ = LINK_O_NONBLOCK,
		NDELAY /*! Open as non-blocking */ = LINK_O_NONBLOCK,
		ACCMODE /*! Access mode mask */ = LINK_O_ACCMODE
	};

	/*! \details List of options for \a whence argument of seek() */
	enum {
		SET = SEEK_SET,
		CURRENT = SEEK_CUR,
		END = SEEK_END

	};

	/*! \details Get the name of the file from a given path */
	static const char * name(const char * path);

	/*! \details Open the specified file or device */
	virtual int open(const char * name, int flags);

	/*! \details IO Ctl requests */
	virtual int ioctl(int req, void * arg) const;

	/*! \details Seek to a location in the file or on the device */
	virtual int seek(int loc, int whence = LINK_SEEK_SET) const;

	/*! \details Returns the location of the cursor in the device or file */
	int loc() const;

	/*! \details Return the current flags for the file */
	int flags() const;

	/*! \details Return the file number for accessing the file or device */
	int fileno() const;

	/*! \details Close the file or device */
	virtual int close();

	/*! \details reads the device. */
	virtual int read(void * buf, int nbyte) const;

	/*! \details writes data to the device */
	virtual int write(const void * buf, int nbyte) const;

#ifndef __link
	/*! \details Asyncronous read */
	virtual int read(sys::Aio & aio) const;
	/*! \details Asyncronous write */
	virtual int write(sys::Aio & aio) const;
#endif

	/*! \details reads the device at the location */
	int read(int loc, void * buf, int nbyte) const;

	/*! \details writes the device at the location
	 *
	 * @param loc Location to read (not application to character devices)
	 * @param buf Pointer to the source data
	 * @param nbyte Number of bytes to write
	 * @return Number of bytes successfully written or -1 with errno set
	 */
	int write(int loc, const void * buf, int nbyte) const;

	/*! \details writes a string to the device */
	int write(const char * s) const { return write(s, strlen(s)); }

	/*! \details Read a line from the device or file */
	int readline(char * buf, int nbyte, int timeout, char term) const;

	/*! \details Write a Var::String to the file
	 *
	 * @param str The string to write
	 * @return The number of bytes written
	 */
	inline int write(const var::String & str) const { return write(str.c_str(), str.size()); }

	enum {
		GETS_BUFFER_SIZE = 128
	};

	/*! \details Read up to n-1 bytes to \a s until end-of-file or \a term is reached.  */
	char * gets(char * s, int n, char term = '\n') const;

	/*! \details Read a line in to the string */
	char * gets(var::String & s, char term = '\n') const { return gets(s.cdata(), s.capacity(), term); }

#ifdef __link
	void set_driver(link_transport_mdriver_t * d){ _driver = d; }
	link_transport_mdriver_t * driver() const { return _driver; }
#endif

	/*! \details ioctl() with request and const arg pointer */
	int ioctl(int req, const void * arg) const { return ioctl(req, (void*)arg); }
	/*! \details ioctl() with just a request */
	int ioctl(int req) const { return ioctl(req, (void*)NULL); }
	/*! \details ioctl() with request and integer arg */
	int ioctl(int req, int arg) const { return ioctl(req, (void*)(size_t)arg); }

protected:
	mutable int fd;

#ifdef __link
	link_transport_mdriver_t * _driver;
#endif

};

};

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


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENCE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CPPA_OPT_HPP
#define CPPA_OPT_HPP

// <backward_compatibility version="0.9" whole_file="yes">
#include <map>
#include <string>
#include <vector>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <functional>

#include "caf/on.hpp"
#include "caf/optional.hpp"

#include "caf/detail/demangle.hpp"

#include "cppa/opt_impls.hpp"

namespace caf {

using string_proj = std::function<optional<std::string> (const std::string&)>;

string_proj extract_longopt_arg(const std::string& prefix) {
  return [prefix](const std::string& arg) -> optional<std::string> {
    if (arg.compare(0, prefix.size(), prefix)) {
      return std::string(arg.begin() + prefix.size(), arg.end());
    }
    return none;
  };
}

/**
 * Right-hand side of a match expression for a program option
 * reading an argument of type `T`.
 */
template <class T>
detail::rd_arg_functor<T> rd_arg(T& storage) {
  return {storage};
}

/**
 * Right-hand side of a match expression for a program option
 * adding an argument of type `T` to `storage`.
 */
template <class T>
detail::add_arg_functor<T> add_arg(std::vector<T>& storage) {
  return {storage};
}

inline std::function<void()> set_flag(bool& storage) {
  return [&] { storage = true; };
}

/**
 * Stores a help text along with the number of expected arguments.
 */
struct option_info {
  std::string help_text;
  size_t num_args;
};

/**
 * Stores a help text for program options with option groups.
 */
using options_description =
  std::map<
    std::string,
    std::map<
      std::pair<char, std::string>,
      option_info
    >
  >;

using opt_rvalue_builder =
  decltype(on(std::function<optional<std::string> (const std::string&)>{})
           || on(std::string{}, val<std::string>));

using opt0_rvalue_builder = decltype(on(std::string{}) || on(std::string{}));

/**
 * Left-hand side of a match expression for a program option with
 *    one argument.
 */
inline opt_rvalue_builder on_opt1(char short_opt,
                  std::string long_opt,
                  options_description* desc = nullptr,
                  std::string help_text = "",
                  std::string help_group = "general options") {
  if (desc) {
    option_info oinf{move(help_text), 1};
    (*desc)[help_group].insert(std::make_pair(
                   std::make_pair(short_opt, long_opt),
                   std::move(oinf)));
  }
  const char short_flag_arr[] = {'-', short_opt, '\0' };
  const char* lhs_str = short_flag_arr;
  std::string prefix = "--";
  prefix += long_opt;
  prefix += "=";
  std::function<optional<std::string> (const std::string&)> kvp =
    [prefix](const std::string& input) -> optional<std::string> {
      if (std::equal(std::begin(prefix), std::end(prefix), std::begin(input))) {
        return input.substr(prefix.size());
      }
      else if (std::equal(std::begin(prefix) + 1, std::end(prefix), std::begin(input))) {
        return input.substr(prefix.size() - 1);
      }
      return none;
    };
  std::vector<std::string> opts;
  opts.push_back(lhs_str);
  opts.push_back("--" + long_opt);
  opts.push_back("-" + long_opt);
  return on(extract_longopt_arg(prefix)) || on(lhs_str, val<std::string>);
}

/**
 * Left-hand side of a match expression for a program option with no argument.
 */
inline opt0_rvalue_builder on_opt0(char short_opt,
                   std::string long_opt,
                   options_description* desc = nullptr,
                   std::string help_text = "",
                   std::string help_group = "general options") {
  if (desc) {
    option_info oinf{help_text, 0};
    (*desc)[help_group].insert(std::make_pair(
                   std::make_pair(short_opt, long_opt),
                   std::move(oinf)));
  }
  const char short_flag_arr[] = {'-', short_opt, '\0' };
  std::string short_opt_string = short_flag_arr;
  return on(long_opt) || on(short_opt_string);
}

/**
 * Returns a function that prints the help text of `desc` to `out`.
 */
std::function<void()> print_desc(options_description* desc,
                 std::ostream& out = std::cout) {
  return [&out, desc] {
    if (!desc) return;
    if (desc->empty()) {
      out << "please use '-h' or '--help' for a list "
           "of available program options\n";
    }
    for (auto& opt_group : *desc) {
      out << opt_group.first << ":\n";
      for (auto& opt : opt_group.second) {
        out << "  ";
        out << std::setw(40) << std::left;
        std::ostringstream tmp;
        auto& names = opt.first;
        if (names.first != '\0') {
          tmp << "-" << names.first;
          for (size_t num = 1; num <= opt.second.num_args; ++num) {
            tmp << " <arg" << num << ">";
          }
          tmp << " | ";
        }
        tmp << "--" << names.second;
        if (opt.second.num_args > 0) {
          tmp << "=<arg1>";
        }
        for (size_t num = 2; num <= opt.second.num_args; ++num) {
          tmp << ", <arg" << num << ">";
        }
        out << tmp.str() << opt.second.help_text << "\n";
      }
      out << "\n";
    }
  };
}

/**
 * Returns a function that prints the help text of `desc` to `out`
 *    and then calls `exit(exit_reason).
 */
inline std::function<void()> print_desc_and_exit(options_description* desc,
                         std::ostream& out = std::cout,
                         int exit_reason = 0) {
  auto fun = print_desc(desc, out);
  return [=] {
    fun();
    exit(exit_reason);
  };
}

} // namespace caf
// </backward_compatibility>

#endif // CPPA_OPT_HPP
<commit_msg>Fix parsing of command line options, close #165<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright (C) 2011 - 2014                                                  *
 * Dominik Charousset <dominik.charousset (at) haw-hamburg.de>                *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENCE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#ifndef CPPA_OPT_HPP
#define CPPA_OPT_HPP

// <backward_compatibility version="0.9" whole_file="yes">
#include <map>
#include <string>
#include <vector>
#include <iomanip>
#include <sstream>
#include <iostream>
#include <functional>

#include "caf/on.hpp"
#include "caf/optional.hpp"

#include "caf/detail/demangle.hpp"

#include "cppa/opt_impls.hpp"

namespace caf {

using string_proj = std::function<optional<std::string> (const std::string&)>;

string_proj extract_longopt_arg(const std::string& prefix) {
  return [prefix](const std::string& arg) -> optional<std::string> {
    if (arg.compare(0, prefix.size(), prefix) == 0) {
      return std::string(arg.begin() + prefix.size(), arg.end());
    }
    return none;
  };
}

/**
 * Right-hand side of a match expression for a program option
 * reading an argument of type `T`.
 */
template <class T>
detail::rd_arg_functor<T> rd_arg(T& storage) {
  return {storage};
}

/**
 * Right-hand side of a match expression for a program option
 * adding an argument of type `T` to `storage`.
 */
template <class T>
detail::add_arg_functor<T> add_arg(std::vector<T>& storage) {
  return {storage};
}

inline std::function<void()> set_flag(bool& storage) {
  return [&] { storage = true; };
}

/**
 * Stores a help text along with the number of expected arguments.
 */
struct option_info {
  std::string help_text;
  size_t num_args;
};

/**
 * Stores a help text for program options with option groups.
 */
using options_description =
  std::map<
    std::string,
    std::map<
      std::pair<char, std::string>,
      option_info
    >
  >;

using opt_rvalue_builder =
  decltype(on(std::function<optional<std::string> (const std::string&)>{})
           || on(std::string{}, val<std::string>));

using opt0_rvalue_builder = decltype(on(std::string{}) || on(std::string{}));

/**
 * Left-hand side of a match expression for a program option with
 *    one argument.
 */
inline opt_rvalue_builder on_opt1(char short_opt,
                  std::string long_opt,
                  options_description* desc = nullptr,
                  std::string help_text = "",
                  std::string help_group = "general options") {
  if (desc) {
    option_info oinf{move(help_text), 1};
    (*desc)[help_group].insert(std::make_pair(
                   std::make_pair(short_opt, long_opt),
                   std::move(oinf)));
  }
  const char short_flag_arr[] = {'-', short_opt, '\0' };
  const char* lhs_str = short_flag_arr;
  std::string prefix = "--";
  prefix += long_opt;
  prefix += "=";
  std::function<optional<std::string> (const std::string&)> kvp =
    [prefix](const std::string& input) -> optional<std::string> {
      if (std::equal(std::begin(prefix), std::end(prefix), std::begin(input))) {
        return input.substr(prefix.size());
      }
      else if (std::equal(std::begin(prefix) + 1, std::end(prefix), std::begin(input))) {
        return input.substr(prefix.size() - 1);
      }
      return none;
    };
  std::vector<std::string> opts;
  opts.push_back(lhs_str);
  opts.push_back("--" + long_opt);
  opts.push_back("-" + long_opt);
  return on(extract_longopt_arg(prefix)) || on(lhs_str, val<std::string>);
}

/**
 * Left-hand side of a match expression for a program option with no argument.
 */
inline opt0_rvalue_builder on_opt0(char short_opt,
                   std::string long_opt,
                   options_description* desc = nullptr,
                   std::string help_text = "",
                   std::string help_group = "general options") {
  if (desc) {
    option_info oinf{help_text, 0};
    (*desc)[help_group].insert(std::make_pair(
                   std::make_pair(short_opt, long_opt),
                   std::move(oinf)));
  }
  const char short_flag_arr[] = {'-', short_opt, '\0' };
  std::string short_opt_string = short_flag_arr;
  return on(long_opt) || on(short_opt_string);
}

/**
 * Returns a function that prints the help text of `desc` to `out`.
 */
std::function<void()> print_desc(options_description* desc,
                 std::ostream& out = std::cout) {
  return [&out, desc] {
    if (!desc) return;
    if (desc->empty()) {
      out << "please use '-h' or '--help' for a list "
           "of available program options\n";
    }
    for (auto& opt_group : *desc) {
      out << opt_group.first << ":\n";
      for (auto& opt : opt_group.second) {
        out << "  ";
        out << std::setw(40) << std::left;
        std::ostringstream tmp;
        auto& names = opt.first;
        if (names.first != '\0') {
          tmp << "-" << names.first;
          for (size_t num = 1; num <= opt.second.num_args; ++num) {
            tmp << " <arg" << num << ">";
          }
          tmp << " | ";
        }
        tmp << "--" << names.second;
        if (opt.second.num_args > 0) {
          tmp << "=<arg1>";
        }
        for (size_t num = 2; num <= opt.second.num_args; ++num) {
          tmp << ", <arg" << num << ">";
        }
        out << tmp.str() << opt.second.help_text << "\n";
      }
      out << "\n";
    }
  };
}

/**
 * Returns a function that prints the help text of `desc` to `out`
 *    and then calls `exit(exit_reason).
 */
inline std::function<void()> print_desc_and_exit(options_description* desc,
                         std::ostream& out = std::cout,
                         int exit_reason = 0) {
  auto fun = print_desc(desc, out);
  return [=] {
    fun();
    exit(exit_reason);
  };
}

} // namespace caf
// </backward_compatibility>

#endif // CPPA_OPT_HPP
<|endoftext|>


<commit_before>#pragma once

#include <unordered_map>
#include <memory>
#include <type_traits>
#include <tuple>

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++98-compat"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#endif  // CLANG

namespace kgr {

template<typename... Types>
struct Dependency {
	using DependenciesTypes = std::tuple<Types...>;
};

using NoDependencies = Dependency<>;

struct Single {
	using ParentTypes = std::tuple<>;
};

template<typename T>
struct Service;

template<typename... Types>
struct Overrides : Single {
	using ParentTypes = std::tuple<Types...>;
};

namespace detail {

enum class enabler {};

template <bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;

template<int ...>
struct seq { };

template<int N, int ...S>
struct seq_gen : seq_gen<N-1, N-1, S...> { };

template<int ...S>
struct seq_gen<0, S...> {
	using type = seq<S...>;
};

struct Holder {};

template<typename T>
struct InstanceHolder final : Holder {
	explicit InstanceHolder(std::shared_ptr<T> instance) : _instance{std::move(instance)} {}

	std::shared_ptr<T> getInstance() const {
		return _instance;
	}

private:
	std::shared_ptr<T> _instance;
};

template<typename T, typename... Args>
struct CallbackHolder final : Holder {
	using callback_t = std::function<std::shared_ptr<T>(Args...)>;

	explicit CallbackHolder(callback_t callback) : _callback{std::move(callback)} {}

	std::shared_ptr<T> operator ()(Args... args) {
		return _callback(args...);
	}
private:
	callback_t _callback;
};

template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
    return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

template <typename T> void type_id() {}
using type_id_fn = void(*)();

} // namespace detail

class Container : public std::enable_shared_from_this<Container> {
	template<typename Condition, typename T = detail::enabler> using enable_if = detail::enable_if_t<Condition::value, T>;
	template<typename Condition, typename T = detail::enabler> using disable_if = detail::enable_if_t<!Condition::value, T>;
	template<typename T> using is_service_single = std::is_base_of<Single, Service<T>>;
	template<typename T> using is_abstract = std::is_abstract<T>;
	template<typename T> using is_base_of_container = std::is_base_of<Container, T>;
	template<typename T> using is_container = std::is_same<T, Container>;
	template<typename T> using dependency_types = typename Service<T>::DependenciesTypes;
	template<typename T> using parent_types = typename Service<T>::ParentTypes;
	template <typename Tuple> using tuple_seq = typename detail::seq_gen<std::tuple_size<Tuple>::value>::type;
	template<int S, typename T> using parent_element = typename std::tuple_element<S, parent_types<T>>::type;
	template<int S, typename Tuple> using tuple_element = typename std::tuple_element<S, Tuple>::type;
	using holder_ptr = std::unique_ptr<detail::Holder>;
	using holder_cont = std::unordered_map<detail::type_id_fn, holder_ptr>;
	constexpr static detail::enabler null = {};

public:
	Container() = default;
	Container(const Container &) = default;
	Container(Container &&) = default;
	Container& operator =(const Container &) = default;
	Container& operator =(Container &&) = default;
	~Container() = default;

	template<typename T>
	void instance(std::shared_ptr<T> service) {
		static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");

		call_save_instance(std::move(service), tuple_seq<parent_types<T>>{});
	}

	template<typename T>
	void instance() {
		static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");

		instance(make_service<T>());
	}

	template<typename T, disable_if<is_abstract<T>> = null, disable_if<is_base_of_container<T>> = null>
	std::shared_ptr<T> service() {
		return get_service<T>();
	}

	template<typename T, enable_if<is_container<T>> = null>
	std::shared_ptr<T> service() {
		return shared_from_this();
	}

	template<typename T, disable_if<is_container<T>> = null, enable_if<is_base_of_container<T>> = null>
	std::shared_ptr<T> service() {
		return std::dynamic_pointer_cast<T>(shared_from_this());
	}

	template<typename T, enable_if<is_abstract<T>> = null>
	std::shared_ptr<T> service() {
		auto it = _services.find(&detail::type_id<T>);

		if (it != _services.end()) {
			auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());

			if (holder) {
				return holder->getInstance();
			}
		}
		return {};
	}

	template<typename T, typename U>
	void callback(U callback) {
		static_assert(!is_service_single<T>::value, "instance does not accept Single Service.");

		save_callback<T, dependency_types<T>>(tuple_seq<dependency_types<T>>{}, callback);
	}

	virtual void init(){}

private:
	template<typename T, enable_if<is_service_single<T>> = null>
	std::shared_ptr<T> get_service() {
		auto it = _services.find(&detail::type_id<T>);

		if (it == _services.end()) {
			auto service = make_service<T>();
			instance(service);

			return service;
		} else {
			auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());

			if (holder) {
				return holder->getInstance();
			}
		}
		return {};
	}

	template<typename T, disable_if<is_service_single<T>> = null>
	std::shared_ptr<T> get_service() {
		return make_service<T>();
	}

	template<typename T, int ...S>
	void call_save_instance(std::shared_ptr<T> service, detail::seq<S...>) {
		save_instance<T, parent_element<S, T>...>(std::move(service));
	}

	template<typename Tuple, int ...S>
	std::tuple<std::shared_ptr<tuple_element<S, Tuple>>...> dependency(detail::seq<S...>) {
		return std::make_tuple(service<tuple_element<S, Tuple>>()...);
	}

	template<typename T, typename Tuple, int ...S>
	std::shared_ptr<T> callback_make_service(detail::seq<S...>, Tuple dependencies) const {
		auto it = _callbacks.find(&detail::type_id<T>);

		if (it != _callbacks.end()) {
			auto holder = static_cast<detail::CallbackHolder<T, tuple_element<S, Tuple>...>*>(it->second.get());
			if (holder) {
				return (*holder)(std::get<S>(dependencies)...);
			}
		}
		return {};
	}

	template<typename T, typename Tuple, int ...S>
	std::shared_ptr<T> make_service(detail::seq<S...> seq, Tuple dependencies) const {
		auto service = callback_make_service<T, Tuple>(seq, dependencies);

		if (service) {
			return service;
		}

		return std::make_shared<T>(std::get<S>(dependencies)...);
	}

	template <typename T>
	std::shared_ptr<T> make_service() {
		auto seq = tuple_seq<dependency_types<T>>{};
		return make_service<T>(seq, dependency<dependency_types<T>>(seq));
	}

	template<typename T, typename ...Others>
	detail::enable_if_t<(sizeof...(Others) > 0), void> save_instance(std::shared_ptr<T> service) {
		save_instance<T>(service);
		save_instance<Others...>(std::move(service));
	}

	template<typename T>
	void save_instance (std::shared_ptr<T> service) {
		_services[&detail::type_id<T>] = detail::make_unique<detail::InstanceHolder<T>>(std::move(service));
	}

	template<typename T, typename Tuple, int ...S, typename U>
	void save_callback (detail::seq<S...>, U callback) {
		_callbacks[&detail::type_id<T>] = detail::make_unique<detail::CallbackHolder<T, std::shared_ptr<tuple_element<S, Tuple>>...>>(callback);
	}

	holder_cont _callbacks;
	holder_cont _services;
};

template<typename T = Container, typename ...Args>
std::shared_ptr<T> make_container(Args&& ...args) {
	static_assert(std::is_base_of<Container, T>::value, "make_container only accept container types.");

	auto container = std::make_shared<T>(std::forward<Args>(args)...);
	container->init();

	return container;
}

}  // namespace kgr

#if defined(__clang__)
#pragma clang diagnostic pop
#endif  // CLANG
<commit_msg>no need to set clang diagnostic anymore<commit_after>#pragma once

#include <unordered_map>
#include <memory>
#include <type_traits>
#include <tuple>

namespace kgr {

template<typename... Types>
struct Dependency {
	using DependenciesTypes = std::tuple<Types...>;
};

using NoDependencies = Dependency<>;

struct Single {
	using ParentTypes = std::tuple<>;
};

template<typename T>
struct Service;

template<typename... Types>
struct Overrides : Single {
	using ParentTypes = std::tuple<Types...>;
};

namespace detail {

enum class enabler {};

template <bool B, typename T>
using enable_if_t = typename std::enable_if<B, T>::type;

template<int ...>
struct seq { };

template<int N, int ...S>
struct seq_gen : seq_gen<N-1, N-1, S...> { };

template<int ...S>
struct seq_gen<0, S...> {
	using type = seq<S...>;
};

struct Holder {};

template<typename T>
struct InstanceHolder final : Holder {
	explicit InstanceHolder(std::shared_ptr<T> instance) : _instance{std::move(instance)} {}

	std::shared_ptr<T> getInstance() const {
		return _instance;
	}

private:
	std::shared_ptr<T> _instance;
};

template<typename T, typename... Args>
struct CallbackHolder final : Holder {
	using callback_t = std::function<std::shared_ptr<T>(Args...)>;

	explicit CallbackHolder(callback_t callback) : _callback{std::move(callback)} {}

	std::shared_ptr<T> operator ()(Args... args) {
		return _callback(args...);
	}
private:
	callback_t _callback;
};

template<typename T, typename ...Args>
std::unique_ptr<T> make_unique( Args&& ...args )
{
    return std::unique_ptr<T>( new T( std::forward<Args>(args)... ) );
}

template <typename T> void type_id() {}
using type_id_fn = void(*)();

} // namespace detail

class Container : public std::enable_shared_from_this<Container> {
	template<typename Condition, typename T = detail::enabler> using enable_if = detail::enable_if_t<Condition::value, T>;
	template<typename Condition, typename T = detail::enabler> using disable_if = detail::enable_if_t<!Condition::value, T>;
	template<typename T> using is_service_single = std::is_base_of<Single, Service<T>>;
	template<typename T> using is_abstract = std::is_abstract<T>;
	template<typename T> using is_base_of_container = std::is_base_of<Container, T>;
	template<typename T> using is_container = std::is_same<T, Container>;
	template<typename T> using dependency_types = typename Service<T>::DependenciesTypes;
	template<typename T> using parent_types = typename Service<T>::ParentTypes;
	template <typename Tuple> using tuple_seq = typename detail::seq_gen<std::tuple_size<Tuple>::value>::type;
	template<int S, typename T> using parent_element = typename std::tuple_element<S, parent_types<T>>::type;
	template<int S, typename Tuple> using tuple_element = typename std::tuple_element<S, Tuple>::type;
	using holder_ptr = std::unique_ptr<detail::Holder>;
	using holder_cont = std::unordered_map<detail::type_id_fn, holder_ptr>;
	constexpr static detail::enabler null = {};

public:
	Container() = default;
	Container(const Container &) = default;
	Container(Container &&) = default;
	Container& operator =(const Container &) = default;
	Container& operator =(Container &&) = default;
	~Container() = default;

	template<typename T>
	void instance(std::shared_ptr<T> service) {
		static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");

		call_save_instance(std::move(service), tuple_seq<parent_types<T>>{});
	}

	template<typename T>
	void instance() {
		static_assert(is_service_single<T>::value, "instance only accept Single Service instance.");

		instance(make_service<T>());
	}

	template<typename T, disable_if<is_abstract<T>> = null, disable_if<is_base_of_container<T>> = null>
	std::shared_ptr<T> service() {
		return get_service<T>();
	}

	template<typename T, enable_if<is_container<T>> = null>
	std::shared_ptr<T> service() {
		return shared_from_this();
	}

	template<typename T, disable_if<is_container<T>> = null, enable_if<is_base_of_container<T>> = null>
	std::shared_ptr<T> service() {
		return std::dynamic_pointer_cast<T>(shared_from_this());
	}

	template<typename T, enable_if<is_abstract<T>> = null>
	std::shared_ptr<T> service() {
		auto it = _services.find(&detail::type_id<T>);

		if (it != _services.end()) {
			auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());

			if (holder) {
				return holder->getInstance();
			}
		}
		return {};
	}

	template<typename T, typename U>
	void callback(U callback) {
		static_assert(!is_service_single<T>::value, "instance does not accept Single Service.");

		save_callback<T, dependency_types<T>>(tuple_seq<dependency_types<T>>{}, callback);
	}

	virtual void init(){}

private:
	template<typename T, enable_if<is_service_single<T>> = null>
	std::shared_ptr<T> get_service() {
		auto it = _services.find(&detail::type_id<T>);

		if (it == _services.end()) {
			auto service = make_service<T>();
			instance(service);

			return service;
		} else {
			auto holder = static_cast<detail::InstanceHolder<T>*>(it->second.get());

			if (holder) {
				return holder->getInstance();
			}
		}
		return {};
	}

	template<typename T, disable_if<is_service_single<T>> = null>
	std::shared_ptr<T> get_service() {
		return make_service<T>();
	}

	template<typename T, int ...S>
	void call_save_instance(std::shared_ptr<T> service, detail::seq<S...>) {
		save_instance<T, parent_element<S, T>...>(std::move(service));
	}

	template<typename Tuple, int ...S>
	std::tuple<std::shared_ptr<tuple_element<S, Tuple>>...> dependency(detail::seq<S...>) {
		return std::make_tuple(service<tuple_element<S, Tuple>>()...);
	}

	template<typename T, typename Tuple, int ...S>
	std::shared_ptr<T> callback_make_service(detail::seq<S...>, Tuple dependencies) const {
		auto it = _callbacks.find(&detail::type_id<T>);

		if (it != _callbacks.end()) {
			auto holder = static_cast<detail::CallbackHolder<T, tuple_element<S, Tuple>...>*>(it->second.get());
			if (holder) {
				return (*holder)(std::get<S>(dependencies)...);
			}
		}
		return {};
	}

	template<typename T, typename Tuple, int ...S>
	std::shared_ptr<T> make_service(detail::seq<S...> seq, Tuple dependencies) const {
		auto service = callback_make_service<T, Tuple>(seq, dependencies);

		if (service) {
			return service;
		}

		return std::make_shared<T>(std::get<S>(dependencies)...);
	}

	template <typename T>
	std::shared_ptr<T> make_service() {
		auto seq = tuple_seq<dependency_types<T>>{};
		return make_service<T>(seq, dependency<dependency_types<T>>(seq));
	}

	template<typename T, typename ...Others>
	detail::enable_if_t<(sizeof...(Others) > 0), void> save_instance(std::shared_ptr<T> service) {
		save_instance<T>(service);
		save_instance<Others...>(std::move(service));
	}

	template<typename T>
	void save_instance (std::shared_ptr<T> service) {
		_services[&detail::type_id<T>] = detail::make_unique<detail::InstanceHolder<T>>(std::move(service));
	}

	template<typename T, typename Tuple, int ...S, typename U>
	void save_callback (detail::seq<S...>, U callback) {
		_callbacks[&detail::type_id<T>] = detail::make_unique<detail::CallbackHolder<T, std::shared_ptr<tuple_element<S, Tuple>>...>>(callback);
	}

	holder_cont _callbacks;
	holder_cont _services;
};

template<typename T = Container, typename ...Args>
std::shared_ptr<T> make_container(Args&& ...args) {
	static_assert(std::is_base_of<Container, T>::value, "make_container only accept container types.");

	auto container = std::make_shared<T>(std::forward<Args>(args)...);
	container->init();

	return container;
}

}  // namespace kgr
<|endoftext|>


<commit_before>#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
#include <atomic>
#include <assert.h>
#include <gtest/gtest.h>
#include <fast-event-system/sync.h>
#include <fast-event-system/async_fast.h>
#include <fast-event-system/async_delay.h>
#include "../parallel.h"
#include "../task.h"
#include "../algorithm.h"

class foo
{
public:
	explicit foo() { std::cout << "constructor empty" << std::endl; }
	explicit foo(const std::string& data) : _data(data) { std::cout << "constructor with parms" << std::endl; }
	virtual ~foo() { std::cout << "destructor" << std::endl; }

	foo(const foo& other) : _data(other._data) { std::cout << "constructor copia" << std::endl; }
	foo& operator=(const foo& other)
	{
		std::cout << "operador asignación" << std::endl;
		foo(other).swap(*this);
		return *this;
	}

	foo(foo&& other) : _data(std::move(other._data)) { std::cout << "constructor de movimiento" << std::endl; }

	foo& operator=(foo&& other)
	{
		std::cout << "operador asignacion de movimiento" << std::endl;
		foo(std::move(other)).swap(*this);
		return *this;
	}

	void swap(foo& other)
	{
		std::cout << "swap" << std::endl;
		using std::swap;
		swap(_data, other._data);
	}

private:
	std::string _data;
};

class ParallelFesTest : testing::Test { };

#if 1

TEST(ParallelFesTest, Test1)
{
	const int N = 100;
	std::atomic<int> total;
	total = 0;
	fes::async_fast<int> channel;
	channel.connect([&total](int data) {
		total += data;
	});
	asyncply::parallel(
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*2);
			}
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*3);
			}
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*4);
			}
		});
	ASSERT_EQ(total, 44550);
}

#endif

TEST(ParallelFesTest, TestSync)
{
	fes::sync<foo> channel;
	channel.connect([](const foo& f) {
		std::cout << "<sync> received foo" << std::endl;
	});
	// send foo
	channel(foo("bar"));
}

TEST(ParallelFesTest, TestAsyncFast)
{
	fes::async_fast<foo> channel;
	channel.connect([](const foo& f) {
		std::cout << "<async_fast> received foo" << std::endl;
	});
	// send foo
	channel(foo("bar"));
	// recv foo
	channel.get();
}

TEST(ParallelFesTest, TestAsyncDelay)
{
	fes::async_delay<foo> channel;
	channel.connect([](auto& f) {
		std::cout << "<async_delay> received foo" << std::endl;
	});
	// send foo
	channel(0, fes::deltatime(0), foo("bar"));
	// recv foo
	channel.get();
}

<commit_msg>Update test_parallel_fes.cpp<commit_after>#include <iostream>
#include <vector>
#include <cmath>
#include <cstring>
#include <atomic>
#include <assert.h>
#include <gtest/gtest.h>
#include <fast-event-system/sync.h>
#include <fast-event-system/async_fast.h>
#include <fast-event-system/async_delay.h>
#include "../parallel.h"
#include "../task.h"
#include "../algorithm.h"

class foo
{
public:
	explicit foo() { std::cout << "constructor empty" << std::endl; }
	explicit foo(const std::string& data) : _data(data) { std::cout << "constructor with parms" << std::endl; }
	virtual ~foo() { std::cout << "destructor" << std::endl; }

	foo(const foo& other) : _data(other._data) { std::cout << "constructor copia" << std::endl; }
	foo& operator=(const foo& other)
	{
		std::cout << "operador asignación" << std::endl;
		foo(other).swap(*this);
		return *this;
	}

	foo(foo&& other) : _data(std::move(other._data)) { std::cout << "constructor de movimiento" << std::endl; }

	foo& operator=(foo&& other)
	{
		std::cout << "operador asignacion de movimiento" << std::endl;
		foo(std::move(other)).swap(*this);
		return *this;
	}

	void swap(foo& other)
	{
		std::cout << "swap" << std::endl;
		using std::swap;
		swap(_data, other._data);
	}

private:
	std::string _data;
};

class ParallelFesTest : testing::Test { };

#if 0

TEST(ParallelFesTest, Test1)
{
	const int N = 100;
	std::atomic<int> total;
	total = 0;
	fes::async_fast<int> channel;
	channel.connect([&total](int data) {
		total += data;
	});
	asyncply::parallel(
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// consumer
			for(int i=0; i<N; ++i)
				channel.get();
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*2);
			}
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*3);
			}
		},
		[&]()
		{
			// producer
			for(int i=0; i<N; ++i)
			{
				channel(i*4);
			}
		});
	ASSERT_EQ(total, 44550);
}

#endif

TEST(ParallelFesTest, TestSync)
{
	fes::sync<foo> channel;
	channel.connect([](const foo& f) {
		std::cout << "<sync> received foo" << std::endl;
	});
	// send foo
	channel(foo("bar"));
}

TEST(ParallelFesTest, TestAsyncFast)
{
	fes::async_fast<foo> channel;
	channel.connect([](const foo& f) {
		std::cout << "<async_fast> received foo" << std::endl;
	});
	// send foo
	channel(foo("bar"));
	// recv foo
	channel.get();
}

TEST(ParallelFesTest, TestAsyncDelay)
{
	fes::async_delay<foo> channel;
	channel.connect([](auto& f) {
		std::cout << "<async_delay> received foo" << std::endl;
	});
	// send foo
	channel(0, fes::deltatime(0), foo("bar"));
	// recv foo
	channel.get();
}

<|endoftext|>


<commit_before>// Copyright 2017, Dawid Kurek, <dawikur@gmail.com>

#ifndef INCLUDE_LEL_BOX_HPP_
#define INCLUDE_LEL_BOX_HPP_

#include <type_traits>

namespace LeL {

template <class Type, Type... Tokens>
struct Box {
 private:
  template <Type... Values>
  struct Self{};

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl {
    static_assert(sizeof...(Tail) != 0, "Index not found");
  };

  template <class Merged, class Left, class Right>
  struct MergeImpl;

 public:
  template <class>
  struct Merge;

  template <Type... NewTokens>
  struct Merge<Box<Type, NewTokens...>> {
    using Result =
      typename MergeImpl<Self<>, Self<Tokens...>, Self<NewTokens...>>::Result;
  };

  template <Type       Token>
  static constexpr int IndexOf() noexcept {
    return IndexOfImpl<0, Token, Tokens...>::Result::value;
  }

  template <Type... NewTokens>
  using IndexesOf = Box<int, (IndexOf<NewTokens>())...>;

 private:
  template <int Index, Type Token, Type Head, Type... Tail>
  struct IndexOfImpl<Index, Token, Head, Tail...>
    : public IndexOfImpl<Index + 1, Token, Tail...> {};

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl<Index, Token, Token, Tail...> {
    using Result = std::integral_constant<int, Index>;
  };

  // Finish: only left
  template <Type... Merged, Type... Left>
  struct MergeImpl<Self<Merged...>, Self<Left...>, Self<>> {
    using Result = Box<Type, Merged..., Left...>;
  };

  // Finish: only right
  template <Type... Merged, Type... Right>
  struct MergeImpl<Self<Merged...>, Self<>, Self<Right...>> {
    using Result = Box<Type, Merged..., Right...>;
  };

  // Finish: nothing left to do
  template <Type... Merged>
  struct MergeImpl<Self<Merged...>, Self<>, Self<>> {
    using Result = Box<Type, Merged...>;
  };

  template <Type Left, Type Right>
  static constexpr Type const Lower = Left < Right ? Left : Right;

  template <class, bool>
  struct PopFrontIf;

  // Compare first elements from Left and Right
  template <Type... Merged,
            Type HeadL,
            Type... TailL,
            Type HeadR,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<HeadL, TailL...>,
                   Self<HeadR, TailR...>>
    : public MergeImpl<Self<Merged..., Lower<HeadL, HeadR>>,
                       typename PopFrontIf<bool, (HeadL <= HeadR)>::
                         template From<HeadL, TailL...>,
                       typename PopFrontIf<bool, (HeadR <= HeadL)>::
                         template From<HeadR, TailR...>> {};

  template <class Dummy>
  struct PopFrontIf<Dummy, true> {
    template <Type, Type... Types>
    using From = Self<Types...>;
  };

  template <class Dummy>
  struct PopFrontIf<Dummy, false> {
    template <Type... Types>
    using From = Self<Types...>;
  };

  // TODO: 2017-07-10 check if sequences are sored & unique
};

template <class Left, class Right>
using Merge = typename Left::template Merge<Right>::Result;

}  // namespace LeL

#endif  // INCLUDE_LEL_BOX_HPP_
<commit_msg>Try to fix gcc<commit_after>// Copyright 2017, Dawid Kurek, <dawikur@gmail.com>

#ifndef INCLUDE_LEL_BOX_HPP_
#define INCLUDE_LEL_BOX_HPP_

#include <type_traits>

namespace LeL {

template <class Type, Type... Tokens>
struct Box {
 private:
  template <Type... Values>
  struct Self;

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl {
    static_assert(sizeof...(Tail) != 0, "Index not found");
  };

  template <class Merged, class Left, class Right>
  struct MergeImpl;

 public:
  template <class>
  struct Merge;

  template <Type... NewTokens>
  struct Merge<Box<Type, NewTokens...>> {
    using Result =
      typename MergeImpl<Self<>, Self<Tokens...>, Self<NewTokens...>>::Result;
  };

  template <Type       Token>
  static constexpr int IndexOf() noexcept {
    return IndexOfImpl<0, Token, Tokens...>::Result::value;
  }

  template <Type... NewTokens>
  using IndexesOf = Box<int, (IndexOf<NewTokens>())...>;

 private:
  template <int Index, Type Token, Type Head, Type... Tail>
  struct IndexOfImpl<Index, Token, Head, Tail...>
    : public IndexOfImpl<Index + 1, Token, Tail...> {};

  template <int Index, Type Token, Type... Tail>
  struct IndexOfImpl<Index, Token, Token, Tail...> {
    using Result = std::integral_constant<int, Index>;
  };

  // Finish: only left
  template <Type... Merged, Type HeadL, Type... TailL>
  struct MergeImpl<Self<Merged...>, Self<HeadL, TailL...>, Self<>> {
    using Result = Box<Type, Merged..., HeadL, TailL...>;
  };

  // Finish: only right
  template <Type... Merged, Type HeadR, Type... TailR>
  struct MergeImpl<Self<Merged...>, Self<>, Self<HeadR, TailR...>> {
    using Result = Box<Type, Merged..., HeadR, TailR...>;
  };

  // Finish: nothing left to do
  template <Type... Merged>
  struct MergeImpl<Self<Merged...>, Self<>, Self<>> {
    using Result = Box<Type, Merged...>;
  };

  template <Type Left, Type Right>
  static constexpr Type const Lower = Left < Right ? Left : Right;

  template <class, bool>
  struct PopFrontIf;

  // Compare first elements from Left and Right
  template <Type... Merged,
            Type HeadL,
            Type... TailL,
            Type HeadR,
            Type... TailR>
  struct MergeImpl<Self<Merged...>,
                   Self<HeadL, TailL...>,
                   Self<HeadR, TailR...>>
    : public MergeImpl<Self<Merged..., Lower<HeadL, HeadR>>,
                       typename PopFrontIf<bool, (HeadL <= HeadR)>::
                         template From<HeadL, TailL...>,
                       typename PopFrontIf<bool, (HeadR <= HeadL)>::
                         template From<HeadR, TailR...>> {};

  template <class Dummy>
  struct PopFrontIf<Dummy, true> {
    template <Type, Type... Types>
    using From = Self<Types...>;
  };

  template <class Dummy>
  struct PopFrontIf<Dummy, false> {
    template <Type... Types>
    using From = Self<Types...>;
  };

  // TODO: 2017-07-10 check if sequences are sored & unique
};

template <class Left, class Right>
using Merge = typename Left::template Merge<Right>::Result;

}  // namespace LeL

#endif  // INCLUDE_LEL_BOX_HPP_
<|endoftext|>


<commit_before>#include "math/StepFunction.hh"

#include "tests/Base.hh"

using namespace aleph::math;

template <class T> void test()
{
  ALEPH_TEST_BEGIN( "Step function: Basic properties" );

  StepFunction<T> f;
  f.add( 0, 1, 1   );
  f.add( 2, 3, 1   );
  f.add( 3, 4, 2 );

  ALEPH_ASSERT_THROW( f(0)   == 1 );
  ALEPH_ASSERT_THROW( f(1)   == 1 );
  ALEPH_ASSERT_THROW( f(1.5) == 0 );
  ALEPH_ASSERT_THROW( f(2)   == 1 );
  ALEPH_ASSERT_THROW( f(3)   == 1 ); // TODO: Incorrect
  ALEPH_ASSERT_THROW( f(3.5) == 2 );

  ALEPH_TEST_END();
}

int main()
{
  test<double>();
  test<float> ();
}
<commit_msg>Started extending step function test<commit_after>#include "math/StepFunction.hh"

#include "persistenceDiagrams/PersistenceDiagram.hh"
#include "persistenceDiagrams/PersistenceIndicatorFunction.hh"

#include "tests/Base.hh"

using namespace aleph::math;

template <class T> void testStepFunction()
{
  ALEPH_TEST_BEGIN( "Step function: Basic properties" );

  StepFunction<T> f;
  f.add( 0, 1, 1   );
  f.add( 2, 3, 1   );
  f.add( 3, 4, 2 );

  ALEPH_ASSERT_THROW( f(0)   == 1 );
  ALEPH_ASSERT_THROW( f(1)   == 1 );
  ALEPH_ASSERT_THROW( f(1.5) == 0 );
  ALEPH_ASSERT_THROW( f(2)   == 1 );
  ALEPH_ASSERT_THROW( f(3)   == 1 ); // TODO: Incorrect
  ALEPH_ASSERT_THROW( f(3.5) == 2 );

  ALEPH_TEST_END();
}

template <class T> void testPersistenceIndicatorFunction()
{
  using PersistenceDiagram = aleph::PersistenceDiagram<T>;

  ALEPH_TEST_BEGIN( "Persistence indicator function" );

  PersistenceDiagram pd1;
  pd1.add(1,   2  );
  pd1.add(1.5, 2.5);
  pd1.add(2,   3  );

  PersistenceDiagram pd2;
  pd2.add(1,   2  );
  pd2.add(3,   4  );

  auto f = aleph::persistenceIndicatorFunction( pd1);
  auto g = aleph::persistenceIndicatorFunction( pd2 );

  ALEPH_TEST_END();
}

int main()
{
  testStepFunction<double>();
  testStepFunction<float> ();

  testPersistenceIndicatorFunction<double>();
  testPersistenceIndicatorFunction<float>();
}
<|endoftext|>


<commit_before>/*
 * Launch and manage WAS child processes.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "was_stock.hxx"
#include "was_quark.h"
#include "was_launch.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_manager.hxx"
#include "async.hxx"
#include "net/ConnectSocket.hxx"
#include "spawn/ChildOptions.hxx"
#include "spawn/JailConfig.hxx"
#include "pool.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <glib.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <stdlib.h>

struct WasChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;

    const ChildOptions &options;

    WasChildParams(const char *_executable_path,
                   ConstBuffer<const char *> _args,
                   const ChildOptions &_options)
        :executable_path(_executable_path), args(_args),
         options(_options) {}

    const char *GetStockKey(struct pool &pool) const;
};

struct WasChild final : PoolStockItem {
    const char *key;

    JailParams jail_params;

    struct jail_config jail_config;

    WasProcess process;
    Event event;

    explicit WasChild(struct pool &_pool, CreateStockItem c)
        :PoolStockItem(_pool, c) {}

    ~WasChild() override;

    void EventCallback(evutil_socket_t fd, short events);

    /* virtual methods from class StockItem */
    bool Borrow(gcc_unused void *ctx) override {
        event.Delete();
        return true;
    }

    bool Release(gcc_unused void *ctx) override {
        static constexpr struct timeval tv = {
            .tv_sec = 300,
            .tv_usec = 0,
        };

        event.Add(tv);
        return true;
    }
};

const char *
WasChildParams::GetStockKey(struct pool &pool) const
{
    const char *key = executable_path;
    for (auto i : args)
        key = p_strcat(&pool, key, " ", i, nullptr);

    for (auto i : options.env)
        key = p_strcat(&pool, key, "$", i, nullptr);

    char options_buffer[4096];
    *options.MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(&pool, key, options_buffer, nullptr);

    return key;
}

static void
was_child_callback(gcc_unused int status, void *ctx)
{
    WasChild *child = (WasChild *)ctx;

    child->process.pid = -1;
}

/*
 * libevent callback
 *
 */

inline void
WasChild::EventCallback(evutil_socket_t fd, short events)
{
    assert(fd == process.control_fd);

    if ((events & EV_TIMEOUT) == 0) {
        char buffer;
        ssize_t nbytes = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            daemon_log(2, "error on idle WAS control connection: %s\n",
                       strerror(errno));
        else if (nbytes > 0)
            daemon_log(2, "unexpected data from idle WAS control connection\n");
    }

    InvokeIdleDisconnect();
    pool_commit();
}

/*
 * stock class
 *
 */

static void
was_stock_create(gcc_unused void *ctx,
                 struct pool &parent_pool, CreateStockItem c,
                 void *info,
                 gcc_unused struct pool &caller_pool,
                 gcc_unused struct async_operation_ref &async_ref)
{
    WasChildParams *params = (WasChildParams *)info;

    auto &pool = *pool_new_linear(&parent_pool, "was_child", 2048);
    auto *child = NewFromPool<WasChild>(pool, pool, c);

    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    child->key = p_strdup(pool, c.GetStockName());

    const ChildOptions &options = params->options;
    if (options.jail.enabled) {
        child->jail_params.CopyFrom(pool, options.jail);

        if (!jail_config_load(&child->jail_config,
                              "/etc/cm4all/jailcgi/jail.conf", &pool)) {
            GError *error = g_error_new(was_quark(), 0,
                                        "Failed to load /etc/cm4all/jailcgi/jail.conf");
            child->InvokeCreateError(error);
            return;
        }
    } else
        child->jail_params.enabled = false;

    GError *error = nullptr;
    if (!was_launch(&child->process, params->executable_path,
                    params->args,
                    options,
                    &error)) {
        child->InvokeCreateError(error);
        return;
    }

    child_register(child->process.pid, child->key,
                   was_child_callback, child);

    child->event.Set(child->process.control_fd, EV_READ|EV_TIMEOUT,
                     MakeEventCallback(WasChild, EventCallback), child);

    child->InvokeCreateSuccess();
}

WasChild::~WasChild()
{
    if (process.pid >= 0)
        child_kill(process.pid);

    if (process.control_fd >= 0)
        event.Delete();

    process.Close();
}

static constexpr StockClass was_stock_class = {
    .create = was_stock_create,
};

/*
 * interface
 *
 */

StockMap *
was_stock_new(struct pool *pool, unsigned limit, unsigned max_idle)
{
    return hstock_new(*pool, was_stock_class, nullptr, limit, max_idle);
}

void
was_stock_get(StockMap *hstock, struct pool *pool,
              const ChildOptions &options,
              const char *executable_path,
              ConstBuffer<const char *> args,
              StockGetHandler &handler,
              struct async_operation_ref &async_ref)
{
    auto params = NewFromPool<WasChildParams>(*pool, executable_path, args,
                                              options);

    hstock_get(*hstock, *pool, params->GetStockKey(*pool), params,
               handler, async_ref);
}

const WasProcess &
was_stock_item_get(const StockItem &item)
{
    auto *child = (const WasChild *)&item;

    return child->process;
}

const char *
was_stock_translate_path(const StockItem &item,
                         const char *path, struct pool *pool)
{
    auto *child = (const WasChild *)&item;

    if (!child->jail_params.enabled)
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = jail_translate_path(&child->jail_config, path,
                                             child->jail_params.home_directory,
                                             pool);
    return jailed != nullptr ? jailed : path;
}
<commit_msg>was/stock: remove redundant "key" attribute, use GetStockName() instead<commit_after>/*
 * Launch and manage WAS child processes.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "was_stock.hxx"
#include "was_quark.h"
#include "was_launch.hxx"
#include "stock/MapStock.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "child_manager.hxx"
#include "async.hxx"
#include "net/ConnectSocket.hxx"
#include "spawn/ChildOptions.hxx"
#include "spawn/JailConfig.hxx"
#include "pool.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Cast.hxx"

#include <daemon/log.h>

#include <glib.h>
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <signal.h>
#include <stdlib.h>

struct WasChildParams {
    const char *executable_path;

    ConstBuffer<const char *> args;

    const ChildOptions &options;

    WasChildParams(const char *_executable_path,
                   ConstBuffer<const char *> _args,
                   const ChildOptions &_options)
        :executable_path(_executable_path), args(_args),
         options(_options) {}

    const char *GetStockKey(struct pool &pool) const;
};

struct WasChild final : PoolStockItem {
    JailParams jail_params;

    struct jail_config jail_config;

    WasProcess process;
    Event event;

    explicit WasChild(struct pool &_pool, CreateStockItem c)
        :PoolStockItem(_pool, c) {}

    ~WasChild() override;

    void EventCallback(evutil_socket_t fd, short events);

    /* virtual methods from class StockItem */
    bool Borrow(gcc_unused void *ctx) override {
        event.Delete();
        return true;
    }

    bool Release(gcc_unused void *ctx) override {
        static constexpr struct timeval tv = {
            .tv_sec = 300,
            .tv_usec = 0,
        };

        event.Add(tv);
        return true;
    }
};

const char *
WasChildParams::GetStockKey(struct pool &pool) const
{
    const char *key = executable_path;
    for (auto i : args)
        key = p_strcat(&pool, key, " ", i, nullptr);

    for (auto i : options.env)
        key = p_strcat(&pool, key, "$", i, nullptr);

    char options_buffer[4096];
    *options.MakeId(options_buffer) = 0;
    if (*options_buffer != 0)
        key = p_strcat(&pool, key, options_buffer, nullptr);

    return key;
}

static void
was_child_callback(gcc_unused int status, void *ctx)
{
    WasChild *child = (WasChild *)ctx;

    child->process.pid = -1;
}

/*
 * libevent callback
 *
 */

inline void
WasChild::EventCallback(evutil_socket_t fd, short events)
{
    assert(fd == process.control_fd);

    if ((events & EV_TIMEOUT) == 0) {
        char buffer;
        ssize_t nbytes = recv(fd, &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            daemon_log(2, "error on idle WAS control connection: %s\n",
                       strerror(errno));
        else if (nbytes > 0)
            daemon_log(2, "unexpected data from idle WAS control connection\n");
    }

    InvokeIdleDisconnect();
    pool_commit();
}

/*
 * stock class
 *
 */

static void
was_stock_create(gcc_unused void *ctx,
                 struct pool &parent_pool, CreateStockItem c,
                 void *info,
                 gcc_unused struct pool &caller_pool,
                 gcc_unused struct async_operation_ref &async_ref)
{
    WasChildParams *params = (WasChildParams *)info;

    auto &pool = *pool_new_linear(&parent_pool, "was_child", 2048);
    auto *child = NewFromPool<WasChild>(pool, pool, c);

    assert(params != nullptr);
    assert(params->executable_path != nullptr);

    const ChildOptions &options = params->options;
    if (options.jail.enabled) {
        child->jail_params.CopyFrom(pool, options.jail);

        if (!jail_config_load(&child->jail_config,
                              "/etc/cm4all/jailcgi/jail.conf", &pool)) {
            GError *error = g_error_new(was_quark(), 0,
                                        "Failed to load /etc/cm4all/jailcgi/jail.conf");
            child->InvokeCreateError(error);
            return;
        }
    } else
        child->jail_params.enabled = false;

    GError *error = nullptr;
    if (!was_launch(&child->process, params->executable_path,
                    params->args,
                    options,
                    &error)) {
        child->InvokeCreateError(error);
        return;
    }

    child_register(child->process.pid, child->GetStockName(),
                   was_child_callback, child);

    child->event.Set(child->process.control_fd, EV_READ|EV_TIMEOUT,
                     MakeEventCallback(WasChild, EventCallback), child);

    child->InvokeCreateSuccess();
}

WasChild::~WasChild()
{
    if (process.pid >= 0)
        child_kill(process.pid);

    if (process.control_fd >= 0)
        event.Delete();

    process.Close();
}

static constexpr StockClass was_stock_class = {
    .create = was_stock_create,
};

/*
 * interface
 *
 */

StockMap *
was_stock_new(struct pool *pool, unsigned limit, unsigned max_idle)
{
    return hstock_new(*pool, was_stock_class, nullptr, limit, max_idle);
}

void
was_stock_get(StockMap *hstock, struct pool *pool,
              const ChildOptions &options,
              const char *executable_path,
              ConstBuffer<const char *> args,
              StockGetHandler &handler,
              struct async_operation_ref &async_ref)
{
    auto params = NewFromPool<WasChildParams>(*pool, executable_path, args,
                                              options);

    hstock_get(*hstock, *pool, params->GetStockKey(*pool), params,
               handler, async_ref);
}

const WasProcess &
was_stock_item_get(const StockItem &item)
{
    auto *child = (const WasChild *)&item;

    return child->process;
}

const char *
was_stock_translate_path(const StockItem &item,
                         const char *path, struct pool *pool)
{
    auto *child = (const WasChild *)&item;

    if (!child->jail_params.enabled)
        /* no JailCGI - application's namespace is the same as ours,
           no translation needed */
        return path;

    const char *jailed = jail_translate_path(&child->jail_config, path,
                                             child->jail_params.home_directory,
                                             pool);
    return jailed != nullptr ? jailed : path;
}
<|endoftext|>