Skip to content

Commit

Permalink
Don't use __declspec(dllimport) on Windows
Browse files Browse the repository at this point in the history
In "export" mode, we continue to use __declspec(dllexport),
but in "import" mode, just leave out the declspec altogether.
This allows gtirb to work both in with GTIRB_BUILD_SHARED_LIBS
either ON or OFF (i.e., dll mode or static mode).
Note that this works as long as we only export functions/methods
and not data.
  • Loading branch information
tjohnson-gt authored and eschulte committed Apr 15, 2020
1 parent f333f1a commit bfb3a1e
Show file tree
Hide file tree
Showing 50 changed files with 1,034 additions and 441 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# 1.3.2

* Access functions for converting to/from protobuf are no longer public in the C++ API.
* The proto library is no longer dllexported.
* GTIRB_EXPORT_API no longer uses dllimport on the client side.

# 1.3.1

* No longer installs Python files by default. Added a new 'install-python'
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ To use `pre-commit`:
forward declarations of classes when possible to avoid including
their definitions.

- Do not introduce variables to the code that would require a client
to dllimport them. Export.hpp does not setup dllimport declarations
for clients. For example, do not add static function-local variables
in inline functions in header files.

### Testing Development

Expand Down
75 changes: 65 additions & 10 deletions include/gtirb/AuxData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ class GTIRB_EXPORT_API AuxData {
/// !brief Retrieve a type-trait-specific Id number.
virtual std::size_t getApiTypeId() const { return UNREGISTERED_API_TYPE_ID; }

protected:
/// \brief The protobuf message type used for serializing AuxData.
using MessageType = proto::AuxData;

Expand All @@ -472,12 +473,44 @@ class GTIRB_EXPORT_API AuxData {
/// \brief Serialize into a protobuf message.
///
/// \param[out] Message A protobuf message representing the AuxData.
virtual void toProtobuf(MessageType* Message) const;
virtual void toProtobuf(MessageType* Message) const {
toProtobuf(Message, this->SF);
}

// This version of protobuf accepts a SerializedForm object to
// serialize rather than serializing AuxData's SerializedForm
// member. This is used by AuxDataImpl to serialize a typed AuxData
// object. We structure it this way to avoid having the AuxDataImpl
// template (which is instantiated in client code) access
// MessageType fields directly, which would introduce
// dllimport/dllexport issues on Windows.
void toProtobuf(MessageType* Message,
const SerializedForm& SFToSerialize) const;

// Utility function for the AuxDataImpl template that allows us to
// check the embedded type name in the serialized protobuf against an
// expected typename without exposing the MessageType to clients.
static bool checkAuxDataMessageType(const AuxData::MessageType& Message,
const std::string& ExpectedName);

// Present for testing purposes only.
void save(std::ostream& Out) const;

// Present for testing purposes only.
static std::unique_ptr<AuxData>
load(std::istream& In, std::unique_ptr<AuxData> (*FPPtr)(const MessageType&));

private:
SerializedForm SF;

friend class AuxDataContainer; // Friend to enable fromProtobuf.
// Enables serialization by AuxDataContainer via containerToProtobuf.
template <typename T> friend typename T::MessageType toProtobuf(const T&);
friend class SerializationTestHarness; // Testing support.
};
/// @endcond

/// @cond INTERNAL
template <class Schema> class AuxDataImpl : public AuxData {
public:
AuxDataImpl() = default;
Expand All @@ -495,33 +528,55 @@ template <class Schema> class AuxDataImpl : public AuxData {

const typename Schema::Type* get() const { return &Object; }

static std::unique_ptr<AuxDataImpl<Schema>>
fromProtobuf(const MessageType& Message) {
private:
static std::unique_ptr<AuxData> fromProtobuf(const MessageType& Message) {
// Check if the serialized type isn't compatible with the type
// we're trying to deserialize to.
if (Message.type_name() !=
auxdata_traits<typename Schema::Type>::type_name()) {
if (!checkAuxDataMessageType(
Message, auxdata_traits<typename Schema::Type>::type_name())) {
return nullptr;
}

// Note: Do not edit Message's contents here. That would introduce
// dllexport/dllimport problems on Windows. Call the base class's
// fromProtobuf function and then unserialize from its
// SerializedForm structure.
auto TypedAuxData = std::make_unique<AuxDataImpl<Schema>>();
AuxData::fromProtobuf(*TypedAuxData, Message);
auxdata_traits<typename Schema::Type>::fromBytes(TypedAuxData->Object,
Message.data().begin());
auxdata_traits<typename Schema::Type>::fromBytes(
TypedAuxData->Object, TypedAuxData->rawData().RawBytes.begin());
return TypedAuxData;
}

/// \brief Serialize into a protobuf message.
///
/// \param Message The Message to serialize into.
virtual void toProtobuf(MessageType* Message) const override {
Message->set_type_name(auxdata_traits<typename Schema::Type>::type_name());
// Note: Do not edit Message's contents here. That would introduce
// dllexport/dllimport problems on Windows. Store to a
// SerializedForm, and then call the base class's toProtobuf
// function.
AuxData::SerializedForm TypedSF;
TypedSF.ProtobufType = auxdata_traits<typename Schema::Type>::type_name();
auxdata_traits<typename Schema::Type>::toBytes(
this->Object, std::back_inserter(*Message->mutable_data()));
this->Object, std::back_inserter(TypedSF.RawBytes));
AuxData::toProtobuf(Message, TypedSF);
}

// Present for testing purposes only.
void save(std::ostream& Out) const { AuxData::save(Out); }

// Present for testing purposes only.
static std::unique_ptr<AuxDataImpl> load([[maybe_unused]] Context& C,
std::istream& In) {
return std::unique_ptr<AuxDataImpl>{
static_cast<AuxDataImpl*>(AuxData::load(In, fromProtobuf).release())};
}

private:
typename Schema::Type Object;

friend class AuxDataContainer; // Friend to enable to/fromProtobuf.
friend class SerializationTestHarness; // Testing support.
};
/// @endcond

Expand Down
53 changes: 16 additions & 37 deletions include/gtirb/AuxDataContainer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

#include <gtirb/AuxData.hpp>
#include <gtirb/Node.hpp>
#include <gtirb/Serialization.hpp>
#include <boost/range/iterator_range.hpp>
#include <type_traits>

Expand Down Expand Up @@ -85,21 +84,8 @@ class GTIRB_EXPORT_API AuxDataContainer : public Node {

/// \brief Register a type to be used with AuxData of the given name.
template <typename Schema> static void registerAuxDataType() {
assert(!TypeMap.Locked &&
"New AuxData types cannot be added at this point.");

if (auto it = TypeMap.Map.find(Schema::Name); it != TypeMap.Map.end()) {
// Failing this assertion indicates that two attempts to
// register the same AuxData name are using different types.
assert(it->second->getApiTypeId() ==
AuxDataImpl<Schema>::staticGetApiTypeId() &&
"Different types registered for the same AuxData name.");
return;
}

TypeMap.Map.insert(
std::make_pair(std::string(Schema::Name),
std::make_unique<AuxDataTypeImpl<Schema>>()));
registerAuxDataTypeInternal(Schema::Name,
std::make_unique<AuxDataTypeImpl<Schema>>());
}

/// \brief Add a new \ref AuxData, transferring ownership.
Expand All @@ -110,11 +96,8 @@ class GTIRB_EXPORT_API AuxDataContainer : public Node {
///
template <typename Schema> void addAuxData(typename Schema::Type&& X) {
// Make sure this type matches a registered type.
[[maybe_unused]] auto TypeEntry = TypeMap.Map.find(Schema::Name);
assert(TypeEntry != TypeMap.Map.end() &&
TypeEntry->second->getApiTypeId() ==
AuxDataImpl<Schema>::staticGetApiTypeId() &&
"Attempting to add AuxData with unregistered or incorrect type.");
checkAuxDataRegistration(Schema::Name,
AuxDataImpl<Schema>::staticGetApiTypeId());
this->AuxDatas[Schema::Name] =
std::make_unique<AuxDataImpl<Schema>>(std::move(X));
}
Expand Down Expand Up @@ -285,8 +268,8 @@ class GTIRB_EXPORT_API AuxDataContainer : public Node {
std::string Key = M.first;

// See if the name for this AuxData is registered.
if (auto Lookup = TypeMap.Map.find(Key); Lookup != TypeMap.Map.end()) {
Val = Lookup->second->fromProtobuf(M.second);
if (const auto* ADT = lookupAuxDataType(Key)) {
Val = ADT->fromProtobuf(M.second);
}

// If it wasn't registered or was registered with an incompatible
Expand All @@ -302,38 +285,34 @@ class GTIRB_EXPORT_API AuxDataContainer : public Node {
}

protected:
AuxDataContainer(Context& C, Kind knd) : Node(C, knd) {
// Once this is called, we outlaw registering new AuxData types.
TypeMap.Locked = true;
}
AuxDataContainer(Context& C, Kind knd);

private:
AuxDataSet AuxDatas;

struct AuxDataType {
virtual ~AuxDataType() = default;
virtual std::unique_ptr<AuxData>
fromProtobuf(const proto::AuxData& Message) = 0;
virtual std::size_t getApiTypeId() = 0;
fromProtobuf(const proto::AuxData& Message) const = 0;
virtual std::size_t getApiTypeId() const = 0;
};

template <typename Schema> struct AuxDataTypeImpl : public AuxDataType {
std::unique_ptr<AuxData>
fromProtobuf(const proto::AuxData& Message) override {
fromProtobuf(const proto::AuxData& Message) const override {
return AuxDataImpl<Schema>::fromProtobuf(Message);
}

std::size_t getApiTypeId() override {
std::size_t getApiTypeId() const override {
return AuxDataImpl<Schema>::staticGetApiTypeId();
}
};

struct AuxDataTypeMap {
bool Locked = false;
std::map<std::string, std::unique_ptr<AuxDataType>> Map;
};

static AuxDataTypeMap TypeMap;
static void registerAuxDataTypeInternal(const char* Name,
std::unique_ptr<AuxDataType> ADT);
static void checkAuxDataRegistration(const char* Name, std::size_t Id);
static const AuxDataType* lookupAuxDataType(const std::string& Name);
friend struct AuxDataTypeMap; // Allows AuxDataTypeMap to use AuxDataType
};
} // namespace gtirb
#endif // GTIRB_AUXDATACONTAINER_H
66 changes: 39 additions & 27 deletions include/gtirb/ByteInterval.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1802,33 +1802,6 @@ class GTIRB_EXPORT_API ByteInterval : public Node {
}

/// @cond INTERNAL
/// \brief The protobuf message type used for serializing ByteInterval.
using MessageType = proto::ByteInterval;

/// \brief Serialize into a protobuf message.
///
/// \param[out] Message Serialize into this message.
///
/// \return void
void toProtobuf(MessageType* Message) const;

/// \brief Construct a ByteInterval from a protobuf message.
///
/// \param C The Context in which the deserialized ByteInterval will be
/// held. \param Message The protobuf message from which to deserialize.
///
/// \return The deserialized ByteInterval object, or null on failure.
static ByteInterval* fromProtobuf(Context& C, Section* Parent,
const MessageType& Message);

/// \brief Populate symbolic expressions from a Protobuf message.
///
/// \param C The Context in which the deserialized SymbolicExpressions will
/// be held.
///
/// \param Message The protobuf message from which to deserialize.
void symbolicExpressionsFromProtobuf(Context& C, const MessageType& Message);

static bool classof(const Node* N) {
return N->getKind() == Kind::ByteInterval;
}
Expand Down Expand Up @@ -1934,6 +1907,42 @@ class GTIRB_EXPORT_API ByteInterval : public Node {
InitSize ? *InitSize : std::distance(Begin, End), Begin, End);
}

/// \brief The protobuf message type used for serializing ByteInterval.
using MessageType = proto::ByteInterval;

/// \brief Serialize into a protobuf message.
///
/// \param[out] Message Serialize into this message.
///
/// \return void
void toProtobuf(MessageType* Message) const;

/// \brief Construct a ByteInterval from a protobuf message.
///
/// \param C The Context in which the deserialized ByteInterval will be
/// held. \param Message The protobuf message from which to deserialize.
///
/// \return The deserialized ByteInterval object, or null on failure.
static ByteInterval* fromProtobuf(Context& C, Section* Parent,
const MessageType& Message);

/// \brief Populate symbolic expressions from a Protobuf message.
///
/// \param C The Context in which the deserialized SymbolicExpressions will
/// be held.
///
/// \param Message The protobuf message from which to deserialize.
void symbolicExpressionsFromProtobuf(Context& C, const MessageType& Message);

// Present for testing purposes only.
void save(std::ostream& Out) const;

// Present for testing purposes only.
static ByteInterval* load(Context& C, std::istream& In);

// Present for testing purposes only.
void loadSymbolicExpressions(Context& C, std::istream& In);

Section* Parent{nullptr};
std::optional<Addr> Address;
uint64_t Size{0};
Expand All @@ -1947,7 +1956,10 @@ class GTIRB_EXPORT_API ByteInterval : public Node {
// Create, etc.
friend class CodeBlock; // Friend to enable CodeBlock::getAddress.
friend class DataBlock; // Friend to enable DataBlock::getAddress.
friend class Module; // Allow Module::fromProtobuf to deserialize symbolic
// expressions.
friend class Node; // Allow Node::mutateIndices, etc. to set indices.
friend class SerializationTestHarness; // Testing support.
};

} // namespace gtirb
Expand Down
25 changes: 0 additions & 25 deletions include/gtirb/CFG.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@
/// \see CFG_GROUP

namespace gtirb {
namespace proto {
class CFG;
}

class CfgNode;

Expand Down Expand Up @@ -281,27 +278,5 @@ GTIRB_EXPORT_API boost::iterator_range<block_iterator> blocks(CFG& Cfg);
GTIRB_EXPORT_API boost::iterator_range<const_block_iterator>
blocks(const CFG& Cfg);

/// @cond INTERNAL
/// \ingroup CFG_GROUP
/// \brief Serialize a \ref CFG into a protobuf message.
///
/// \param Cfg The CFG to serialize.
///
/// \return A protobuf message representing the \ref CFG and its
/// component blocks (\ref Block).
GTIRB_EXPORT_API proto::CFG toProtobuf(const CFG& Cfg);

/// \ingroup CFG_GROUP
/// \brief Initialize a \ref CFG from a protobuf message.
///
/// \param C The Context in which the deserialized CFG will be held.
/// \param Message The protobuf message from which to deserialize.
/// \param[out] Result The CFG to initialize.
///
/// \return void
GTIRB_EXPORT_API void fromProtobuf(Context& C, CFG& Result,
const proto::CFG& Message);
/// @endcond
} // namespace gtirb

#endif // GTIRB_CFG_H
Loading

0 comments on commit bfb3a1e

Please sign in to comment.