Skip to content

Commit

Permalink
Split message factory into static and non-static parts
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Carroll <[email protected]>
  • Loading branch information
mjcarroll committed May 11, 2023
1 parent b1127ff commit d92045e
Show file tree
Hide file tree
Showing 8 changed files with 581 additions and 388 deletions.
178 changes: 66 additions & 112 deletions core/include/gz/msgs/Factory.hh
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,6 @@
#ifndef GZ_MSGS_FACTORY_HH_
#define GZ_MSGS_FACTORY_HH_

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4100 4512 4127 4068 4244 4267 4251 4146)
#endif
#include <google/protobuf/message.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include <string>
#include <map>
#include <memory>
Expand All @@ -34,118 +25,81 @@
#include "gz/msgs/config.hh"
#include "gz/msgs/Export.hh"

namespace gz
#include "gz/msgs/MessageFactory.hh"

namespace gz::msgs
{
namespace msgs
{
// Inline bracket to help doxygen filtering.
inline namespace GZ_MSGS_VERSION_NAMESPACE {
//
/// \typedef FactoryFn
/// \brief Prototype for message factory generation
typedef std::unique_ptr<google::protobuf::Message> (*FactoryFn) ();
// Inline bracket to help doxygen filtering.
inline namespace GZ_MSGS_VERSION_NAMESPACE {

/// \class Factory Factory.hh gz/msgs.hh
/// \brief A factory that generates protobuf message based on a string type.
/// This class will also try to load all Protobuf descriptors specified
/// in the GZ_DESCRIPTOR_PATH environment variable on program start.
class GZ_MSGS_VISIBLE Factory
{
/// \brief Register a message.
/// \param[in] _msgType Type of message to register.
/// \param[in] _factoryfn Function that generates the message.
public: static void Register(const std::string &_msgType,
FactoryFn _factoryfn);
/// \class Factory Factory.hh gz/msgs.hh
/// \brief A factory that generates protobuf message based on a string type.
/// This class will also try to load all Protobuf descriptors specified
/// in the GZ_DESCRIPTOR_PATH environment variable on program start.
class GZ_MSGS_VISIBLE Factory
{
public: using FactoryFn = MessageFactory::FactoryFn;
public: using FactoryFnCollection = MessageFactory::FactoryFnCollection;
public: using MessagePtr = MessageFactory::MessagePtr;

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
static std::unique_ptr<T> New(const std::string &_msgType)
{
auto msgType = _msgType;
if (msgType.find("ignition") == 0)
{
msgType.replace(0, 8, "gz");
std::cerr << "Trying to create deprecated message type ["
<< _msgType << "]. Using [" << msgType
<< "] instead." << std::endl;
}
return std::unique_ptr<T>(
static_cast<T*>(New(msgType).release()));
}
private: Factory() = default;
public: Factory(const Factory&) = delete;
public: void operator=(const Factory&) = delete;
public: Factory(Factory&&) = delete;
public: void operator=(Factory&&) = delete;
public: static MessageFactory& Instance();

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
static std::unique_ptr<T> New(const std::string &_msgType,
const std::string &_args)
{
auto msgType = _msgType;
if (msgType.find("ignition") == 0)
{
msgType.replace(0, 8, "gz");
std::cerr << "Trying to create deprecated message type ["
<< _msgType << "]. Using [" << msgType
<< "] instead." << std::endl;
}
return std::unique_ptr<T>(
static_cast<T*>(New(msgType, _args).release()));
}
/// \brief Register a message.
/// \param[in] _msgType Type of message to register.
/// \param[in] _factoryfn Function that generates the message.
public: static void Register(const std::string &_msgType,
FactoryFn _factoryfn);

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: static std::unique_ptr<google::protobuf::Message> New(
const std::string &_msgType);
/// \brief Register a collection of messages.
/// \param[in] _functions message generation functions
/// \return Number of registered message types
public: static int RegisterCollection(FactoryFnCollection &_functions);

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: static std::unique_ptr<google::protobuf::Message> New(
const std::string &_msgType, const std::string &_args);
/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
static std::unique_ptr<T> New(const std::string &_msgType)
{
return Factory::Instance().New<T>(_msgType);
}

/// \brief Get all the message types
/// \param[out] _types Vector of strings of the message types.
public: static void Types(std::vector<std::string> &_types);
/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
static std::unique_ptr<T> New(const std::string &_msgType,
const std::string &_args)
{
return Factory::Instance().New<T>(_msgType, _args);
}

/// \brief Load a collection of descriptor .desc files.
/// \param[in] _paths A set of directories containing .desc decriptor
/// files. Each directory should be separated by ":".
public: static void LoadDescriptors(const std::string &_paths);
/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: static MessagePtr New(const std::string &_msgType);

/// \brief A list of registered message types
private: static std::map<std::string, FactoryFn> *msgMap;
};
/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: static MessagePtr
New(const std::string &_msgType, const std::string &_args);

/// \brief Static message registration macro
///
/// Use this macro to register messages.
/// \param[in] _msgtype Message type name.
/// \param[in] _classname Class name for message.
#define GZ_REGISTER_STATIC_MSG(_msgtype, _classname) \
GZ_MSGS_VISIBLE \
std::unique_ptr<google::protobuf::Message> New##_classname() \
{ \
return std::unique_ptr<gz::msgs::_classname>(\
new gz::msgs::_classname); \
} \
class GZ_MSGS_VISIBLE GzMsg##_classname \
{ \
public: GzMsg##_classname() \
{ \
gz::msgs::Factory::Register(_msgtype, New##_classname);\
} \
}; \
static GzMsg##_classname GzMessagesInitializer##_classname;
}
}
/// \brief Get all the message types
/// \param[out] _types Vector of strings of the message types.
public: static void Types(std::vector<std::string> &_types);
};
}
} // namespace gz::msgs
#endif
112 changes: 112 additions & 0 deletions core/include/gz/msgs/MessageFactory.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) 2016 Open Source Robotics Foundation
*
* 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 GZ_MSGS_MESSAGE_FACTORY_HH_
#define GZ_MSGS_MESSAGE_FACTORY_HH_

#include <string>
#include <map>
#include <memory>
#include <vector>

#include "gz/msgs/config.hh"
#include "gz/msgs/Export.hh"
#include "gz/msgs/detail/dynamic_message_cast.hh"

namespace gz::msgs {
/// Forward declarations
class DynamicFactory;

// Inline bracket to help doxygen filtering.
inline namespace GZ_MSGS_VERSION_NAMESPACE {

class GZ_MSGS_VISIBLE MessageFactory
{
public: using Message = google::protobuf::Message;
public: using MessagePtr = std::unique_ptr<Message>;
public: using FactoryFn = std::function<MessagePtr(void)>;
public: using FactoryFnCollection = std::map<std::string, FactoryFn>;

/// \brief Constructor
public: MessageFactory();

/// \brief Destructor
public: ~MessageFactory();

/// \brief Register a message.
/// \param[in] _msgType Type of message to register.
/// \param[in] _factoryfn Function that generates the message.
public: void Register(const std::string &_msgType, FactoryFn _factoryFn);

/// \brief Register a collection of messages.
/// \param[in] _functions Collection of messages to register.
/// \return Number of registered message types
public: int RegisterCollection(FactoryFnCollection &_funtions);

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
std::unique_ptr<T> New(const std::string &_msgType)
{
return detail::dynamic_message_cast<T>(New(_msgType));
}

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: template<typename T>
std::unique_ptr<T> New(const std::string &_msgType,
const std::string &_args)
{
return detail::dynamic_message_cast<T>(New(_msgType, _args));
}

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: MessagePtr New(const std::string &_msgType);

/// \brief Create a new instance of a message.
/// \param[in] _msgType Type of message to create.
/// \param[in] _args Message arguments. This will populate the message.
/// \return Pointer to a google protobuf message. Null if the message
/// type could not be handled.
public: MessagePtr New(
const std::string &_msgType, const std::string &_args);

/// \brief Get all the message types
/// \param[out] _types Vector of strings of the message types.
public: void Types(std::vector<std::string> &_types);

/// \brief Load a collection of descriptor .desc files.
/// \param[in] _paths A set of directories containing .desc decriptor
/// files. Each directory should be separated by ":".
public: void LoadDescriptors(const std::string &_paths);

/// \brief A list of registered message types
private: FactoryFnCollection msgMap;

/// \brief Pointer to dynamic factory implementation
private: std::unique_ptr<gz::msgs::DynamicFactory> dynamicFactory;
};
}
} // namespace gz::msgs
#endif // GZ_MSGS_MESSAGE_FACTORY_HH_
49 changes: 49 additions & 0 deletions core/include/gz/msgs/detail/dynamic_message_cast.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2023 Open Source Robotics Foundation
*
* 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 GZ_MSGS_DETAIL_DYNAMIC_POINTER_CAST_HH_
#define GZ_MSGS_DETAIL_DYNAMIC_POINTER_CAST_HH_

#include <memory>

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4100 4512 4127 4068 4244 4267 4251 4146)
#endif
#include <google/protobuf/message.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif

namespace gz::msgs::detail
{

/// Cast a base unique pointer to protobuf message type to child type
template<typename MsgT>
std::unique_ptr<MsgT>
dynamic_message_cast(std::unique_ptr<google::protobuf::Message> &&_baseMsg)
{
auto converted = std::unique_ptr<MsgT>{dynamic_cast<MsgT*>(_baseMsg.get())};
if (converted) {
(void) _baseMsg.release();
}
return converted;
}

} // namespace gz::msgs::detail

#endif // GZ_MSGS_DETAIL_DYNAMIC_POINTER_CAST_HH_
2 changes: 2 additions & 0 deletions core/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ gz_install_includes(
gz_create_core_library(SOURCES
${gen_sources}
Factory.cc
MessageFactory.cc
DynamicFactory.cc
gz.cc
Utility.cc
)
Expand Down
Loading

0 comments on commit d92045e

Please sign in to comment.