Program Listing for File parameter.hpp

Return to documentation for file (foxglove/include/foxglove/server/parameter.hpp)

#pragma once

#include <foxglove/error.hpp>

#include <algorithm>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <variant>
#include <vector>

struct foxglove_parameter_value;
struct foxglove_parameter;
struct foxglove_parameter_array;

namespace foxglove {

enum class ParameterType : uint8_t {
  None,
  ByteArray,
  Float64,
  Float64Array,
};

class ParameterValueView final {
public:
  using Array = std::vector<ParameterValueView>;
  using Dict = std::map<std::string, ParameterValueView>;
  using Value = std::variant<double, bool, std::string_view, Array, Dict>;

  [[nodiscard]] class ParameterValue clone() const;

  [[nodiscard]] Value value() const;

  template<typename T>
  [[nodiscard]] bool is() const noexcept {
    try {
      auto value = this->value();
      return std::holds_alternative<T>(value);
    } catch (...) {
      // Unknown value tag.
      return false;
    }
  }

  template<typename T>
  [[nodiscard]] T get() const {
    return std::get<T>(this->value());
  }

private:
  friend class ParameterView;
  friend class ParameterValue;

  const foxglove_parameter_value* impl_;

  explicit ParameterValueView(const foxglove_parameter_value* ptr)
      : impl_(ptr) {}
};

// Template specializations for ParameterValueView
//
// Documented manually as tparams because specializations are merged:
// https://github.com/doxygen/doxygen/issues/9468




template<>
[[nodiscard]] inline bool ParameterValueView::is<std::string>() const noexcept {
  return this->is<std::string_view>();
}
template<>
[[nodiscard]] inline ParameterValueView ParameterValueView::get<ParameterValueView>() const {
  return *this;
}
template<>
[[nodiscard]] inline std::string ParameterValueView::get<std::string>() const {
  auto sv = this->get<std::string_view>();
  return std::string(sv);
}

class ParameterValue final {
public:
  explicit ParameterValue(double value);
  explicit ParameterValue(bool value);
  explicit ParameterValue(std::string_view value);
  explicit ParameterValue(const char* value)
      : ParameterValue(std::string_view(value)) {}
  explicit ParameterValue(std::vector<ParameterValue> values);
  explicit ParameterValue(std::map<std::string, ParameterValue> values);

  ~ParameterValue() = default;
  ParameterValue(ParameterValue&& other) noexcept = default;
  ParameterValue& operator=(ParameterValue&& other) noexcept = default;
  ParameterValue(const ParameterValue&) = delete;
  ParameterValue& operator=(const ParameterValue&) = delete;

  [[nodiscard]] ParameterValue clone() const {
    return this->view().clone();
  }

  [[nodiscard]] ParameterValueView view() const noexcept;

  template<typename T>
  [[nodiscard]] bool is() const {
    return this->view().is<T>();
  }

  template<typename T>
  [[nodiscard]] T get() const {
    return this->view().get<T>();
  }

private:
  friend class ParameterValueView;
  friend class Parameter;

  struct Deleter {
    void operator()(foxglove_parameter_value* ptr) const noexcept;
  };

  std::unique_ptr<foxglove_parameter_value, Deleter> impl_;

  explicit ParameterValue(foxglove_parameter_value* ptr);

  [[nodiscard]] foxglove_parameter_value* release() noexcept;
};

class ParameterView final {
public:
  [[nodiscard]] class Parameter clone() const;

  [[nodiscard]] std::string_view name() const noexcept;
  [[nodiscard]] ParameterType type() const noexcept;
  [[nodiscard]] std::optional<ParameterValueView> value() const noexcept;
  [[nodiscard]] bool hasValue() const noexcept {
    return this->value().has_value();
  };

  template<typename T>
  [[nodiscard]] bool is() const noexcept {
    auto value = this->value();
    return value.has_value() && value->is<T>();
  }

  template<typename T>
  [[nodiscard]] bool isArray() const noexcept {
    if (!this->isArray<ParameterValueView>()) {
      return false;
    }
    try {
      const auto& arr = this->get<ParameterValueView::Array>();
      return std::all_of(arr.begin(), arr.end(), [](const ParameterValueView& elem) noexcept {
        return elem.is<T>();
      });
    } catch (...) {
      return false;
    }
  }

  template<typename T>
  [[nodiscard]] bool isDict() const noexcept {
    if (!this->isDict<ParameterValueView>()) {
      return false;
    }
    try {
      const auto& dict = this->get<ParameterValueView::Dict>();
      return std::all_of(
        dict.begin(),
        dict.end(),
        [](const std::pair<std::string, ParameterValueView>& elem) noexcept {
          return elem.second.is<T>();
        }
      );
    } catch (...) {
      return false;
    }
  }

  [[nodiscard]] bool isByteArray() const noexcept;

  template<typename T>
  [[nodiscard]] T get() const {
    auto value = this->value();
    if (!value) {
      throw std::bad_optional_access();
    }
    return value->template get<T>();
  }

  template<typename T>
  [[nodiscard]] std::vector<T> getArray() const {
    auto value = this->value();
    if (!value) {
      throw std::bad_optional_access();
    }
    const auto& arr = value->get<ParameterValueView::Array>();
    std::vector<T> result;
    result.reserve(arr.size());
    for (const auto& elem : arr) {
      result.push_back(elem.get<T>());
    }
    return result;
  }

  template<typename T>
  [[nodiscard]] std::map<std::string, T> getDict() const {
    auto value = this->value();
    if (!value) {
      throw std::bad_optional_access();
    }
    const auto& dict = value->get<ParameterValueView::Dict>();
    std::map<std::string, T> result;
    for (const auto& [dictKey, dictValue] : dict) {
      result.emplace(dictKey, dictValue.get<T>());
    }
    return result;
  }

  [[nodiscard]] FoxgloveResult<std::vector<std::byte>> getByteArray() const;

private:
  friend class Parameter;
  friend class ParameterArrayView;

  const foxglove_parameter* impl_;

  explicit ParameterView(const foxglove_parameter* ptr)
      : impl_(ptr) {}
};

template<>
[[nodiscard]] inline bool ParameterView::isArray<ParameterValueView>() const noexcept {
  auto value = this->value();
  return value.has_value() && value->is<ParameterValueView::Array>();
}

template<>
[[nodiscard]] inline bool ParameterView::isDict<ParameterValueView>() const noexcept {
  auto value = this->value();
  return value.has_value() && value->is<ParameterValueView::Dict>();
}

// Template specializations for ParameterView
//
// Documented manually as tparams because specializations are merged:
// https://github.com/doxygen/doxygen/issues/9468





template<>
[[nodiscard]] inline bool ParameterView::is<std::string_view>() const noexcept {
  auto value = this->value();
  return value.has_value() && value->is<std::string_view>() &&
         this->type() != ParameterType::ByteArray;
}

template<>
[[nodiscard]] inline bool ParameterView::is<std::string>() const noexcept {
  return this->is<std::string_view>();
}

template<>
[[nodiscard]] inline bool ParameterView::is<std::vector<std::byte>>() const noexcept {
  auto value = this->value();
  return value.has_value() && value->is<std::string_view>() &&
         this->type() == ParameterType::ByteArray;
}

template<>
[[nodiscard]] inline bool ParameterView::is<std::vector<double>>() const noexcept {
  return this->isArray<double>();
}

[[nodiscard]] inline bool ParameterView::isByteArray() const noexcept {
  return this->is<std::vector<std::byte>>();
}

template<>
[[nodiscard]] inline std::vector<std::byte> ParameterView::get() const {
  auto result = this->getByteArray();
  if (!result.has_value()) {
    throw std::runtime_error(strerror(result.error()));
  }
  return result.value();
}

template<>
[[nodiscard]] inline std::vector<double> ParameterView::get() const {
  return this->getArray<double>();
}

template<>
[[nodiscard]] inline std::vector<ParameterValueView> ParameterView::get() const {
  return this->getArray<ParameterValueView>();
}

template<>
[[nodiscard]] inline std::map<std::string, ParameterValueView> ParameterView::get() const {
  return this->getDict<ParameterValueView>();
}

class Parameter final {
public:
  explicit Parameter(std::string_view name);
  explicit Parameter(std::string_view name, double value);
  explicit Parameter(std::string_view name, bool value);
  explicit Parameter(std::string_view name, std::string_view value);
  explicit Parameter(std::string_view name, const char* value)
      : Parameter(name, std::string_view(value)) {}
  explicit Parameter(std::string_view name, const uint8_t* data, size_t data_length);
  explicit Parameter(std::string_view name, const std::vector<std::byte>& bytes)
      : Parameter(name, reinterpret_cast<const uint8_t*>(bytes.data()), bytes.size()) {}
  explicit Parameter(std::string_view name, const std::vector<double>& values);
  explicit Parameter(std::string_view name, std::map<std::string, ParameterValue> values);
  explicit Parameter(std::string_view name, ParameterType type, ParameterValue&& value);

  ~Parameter() = default;
  Parameter(Parameter&& other) noexcept = default;
  Parameter& operator=(Parameter&& other) noexcept = default;
  Parameter(const Parameter&) = delete;
  Parameter& operator=(const Parameter&) = delete;

  [[nodiscard]] Parameter clone() const {
    return this->view().clone();
  }

  [[nodiscard]] ParameterView view() const noexcept;

  [[nodiscard]] std::string_view name() const noexcept {
    return this->view().name();
  }
  [[nodiscard]] ParameterType type() const noexcept {
    return this->view().type();
  }
  [[nodiscard]] std::optional<ParameterValueView> value() const noexcept {
    return this->view().value();
  }
  [[nodiscard]] bool hasValue() const noexcept {
    return this->view().hasValue();
  };

  template<typename T>
  [[nodiscard]] bool is() const {
    return this->view().is<T>();
  }

  template<typename T>
  [[nodiscard]] bool isArray() const noexcept {
    return this->view().isArray<T>();
  }

  template<typename T>
  [[nodiscard]] bool isDict() const noexcept {
    return this->view().isDict<T>();
  }

  [[nodiscard]] bool isByteArray() const noexcept {
    return this->view().isByteArray();
  }

  template<typename T>
  [[nodiscard]] T get() const {
    return this->view().get<T>();
  }

  template<typename T>
  [[nodiscard]] std::vector<T> getArray() const {
    return this->view().getArray<T>();
  }

  template<typename T>
  [[nodiscard]] std::map<std::string, T> getDict() const {
    return this->view().getDict<T>();
  }

  [[nodiscard]] FoxgloveResult<std::vector<std::byte>> getByteArray() const {
    return this->view().getByteArray();
  }

private:
  friend class ParameterView;
  friend class ParameterArray;

  struct Deleter {
    void operator()(foxglove_parameter* ptr) const noexcept;
  };

  std::unique_ptr<foxglove_parameter, Deleter> impl_;

  explicit Parameter(foxglove_parameter* param);

  [[nodiscard]] foxglove_parameter* release() noexcept;
};

class ParameterArrayView final {
public:
  explicit ParameterArrayView(const foxglove_parameter_array* ptr);

  [[nodiscard]] std::vector<ParameterView> parameters() const;

private:
  const foxglove_parameter_array* impl_;
};

class ParameterArray final {
public:
  explicit ParameterArray(std::vector<Parameter>&& params);

  ~ParameterArray() = default;
  ParameterArray(ParameterArray&& other) noexcept = default;
  ParameterArray& operator=(ParameterArray&& other) noexcept = default;
  ParameterArray(const ParameterArray&) = delete;
  ParameterArray& operator=(const ParameterArray&) = delete;

  [[nodiscard]] ParameterArrayView view() const noexcept;
  [[nodiscard]] std::vector<ParameterView> parameters() const {
    return this->view().parameters();
  }

private:
  friend class WebSocketServer;

  struct Deleter {
    void operator()(foxglove_parameter_array* ptr) const noexcept;
  };

  std::unique_ptr<foxglove_parameter_array, Deleter> impl_;

  [[nodiscard]] foxglove_parameter_array* release() noexcept;
};

}  // namespace foxglove