Skip to content

Commit

Permalink
Generate 'operator<<' Implementation for Slice-generated Enums (#3372)
Browse files Browse the repository at this point in the history
  • Loading branch information
InsertCreativityHere authored Jan 21, 2025
1 parent 801fce3 commit bdf764b
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 65 deletions.
30 changes: 0 additions & 30 deletions cpp/include/DataStorm/DataStorm.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,36 +118,6 @@ namespace DataStorm
std::shared_ptr<DataStormI::SampleT<Key, Value, UpdateTag>> _impl;
};

/**
* Convert the given sample type to a string and add it to the stream.
*
* @param os The output stream
* @param sampleType The sample type to add to the stream
* @return The output stream
*/
inline std::ostream& operator<<(std::ostream& os, SampleEvent sampleType)
{
switch (sampleType)
{
case SampleEvent::Add:
os << "Add";
break;
case SampleEvent::Update:
os << "Update";
break;
case SampleEvent::Remove:
os << "Remove";
break;
case SampleEvent::PartialUpdate:
os << "PartialUpdate";
break;
default:
os << static_cast<int>(sampleType);
break;
}
return os;
}

/**
* Convert the given sample type vector to a string and add it to the stream.
*
Expand Down
10 changes: 0 additions & 10 deletions cpp/include/Ice/StreamHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,6 @@
# include <span>
#endif

namespace std
{
// TODO: temporary
template<class T, std::enable_if_t<std::is_enum_v<T>, bool> = true>
inline std::ostream& operator<<(std::ostream& os, T value)
{
return os << static_cast<int>(static_cast<typename std::underlying_type<T>::type>(value));
}
}

namespace Ice
{
/// \cond STREAM
Expand Down
46 changes: 41 additions & 5 deletions cpp/src/slice2cpp/Gen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,7 @@ Slice::Gen::generate(const UnitPtr& p)
}

{
ForwardDeclVisitor forwardDeclVisitor(H);
ForwardDeclVisitor forwardDeclVisitor(H, C, _dllExport);
p->visit(&forwardDeclVisitor);

DefaultFactoryVisitor defaultFactoryVisitor(C);
Expand Down Expand Up @@ -876,7 +876,7 @@ Slice::Gen::validateMetadata(const UnitPtr& u)

// "cpp:custom-print"
MetadataInfo customPrintInfo = {
.validOn = {typeid(Struct), typeid(ClassDecl), typeid(Slice::Exception)},
.validOn = {typeid(Struct), typeid(ClassDecl), typeid(Slice::Exception), typeid(Enum)},
.acceptedArgumentKind = MetadataArgumentKind::NoArguments,
};
knownMetadata.emplace("cpp:custom-print", std::move(customPrintInfo));
Expand Down Expand Up @@ -1085,7 +1085,13 @@ Slice::Gen::getSourceExt(const string& file, const UnitPtr& ut)
return dc->getMetadataArgs("cpp:source-ext").value_or("");
}

Slice::Gen::ForwardDeclVisitor::ForwardDeclVisitor(Output& h) : H(h), _useWstring(TypeContext::None) {}
Slice::Gen::ForwardDeclVisitor::ForwardDeclVisitor(Output& h, Output& c, string dllExport)
: H(h),
C(c),
_dllExport(std::move(dllExport)),
_useWstring(TypeContext::None)
{
}

bool
Slice::Gen::ForwardDeclVisitor::visitModuleStart(const ModulePtr& p)
Expand Down Expand Up @@ -1128,8 +1134,10 @@ Slice::Gen::ForwardDeclVisitor::visitInterfaceDecl(const InterfaceDeclPtr& p)
void
Slice::Gen::ForwardDeclVisitor::visitEnum(const EnumPtr& p)
{
string mappedName = p->mappedName();

writeDocSummary(H, p);
H << nl << "enum class " << getDeprecatedAttribute(p) << p->mappedName();
H << nl << "enum class " << getDeprecatedAttribute(p) << mappedName;
H << " : ::std::" << (p->maxValue() <= numeric_limits<uint8_t>::max() ? "uint8_t" : "int32_t");

if (p->maxValue() > numeric_limits<uint8_t>::max() && p->maxValue() <= numeric_limits<int16_t>::max())
Expand Down Expand Up @@ -1166,7 +1174,35 @@ Slice::Gen::ForwardDeclVisitor::visitEnum(const EnumPtr& p)
H << ',';
}
}
H << eb << ';' << sp;
H << eb << ';';

H << nl << _dllExport << "::std::ostream& operator<<(::std::ostream&, " << mappedName << ");" << sp;

if (!p->hasMetadata("cpp:custom-print"))
{
// We generate the implementation unless custom-print tells us not to.
// If the provided value corresponds to a named enumerator value, we print the corresponding name.
// Otherwise, we print the underlying integer value.
C << sp << nl << "::std::ostream&";
C << nl << p->mappedScope().substr(2) << "operator<<(::std::ostream& os, " << mappedName << " value)";
C << sb;
C << nl << "switch (value)";
C << sb;
for (const auto& enumerator : enumerators)
{
const string enumeratorName = enumerator->mappedName();
C << nl << "case " << mappedName << "::" << enumeratorName << ":";
C.inc();
C << nl << "return os << \"" + enumeratorName + "\";";
C.dec();
}
C << nl << "default:";
C.inc();
C << nl << "return os << static_cast<::std::int32_t>(value);";
C.dec();
C << eb;
C << eb;
}
}

void
Expand Down
31 changes: 17 additions & 14 deletions cpp/src/slice2cpp/Gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ namespace Slice
class ForwardDeclVisitor final : public ParserVisitor
{
public:
ForwardDeclVisitor(IceInternal::Output&);
ForwardDeclVisitor(IceInternal::Output&, IceInternal::Output&, std::string);
ForwardDeclVisitor(const ForwardDeclVisitor&) = delete;

bool visitModuleStart(const ModulePtr&) final;
Expand All @@ -76,6 +76,9 @@ namespace Slice

private:
IceInternal::Output& H;
IceInternal::Output& C;

std::string _dllExport;
TypeContext _useWstring;
std::list<TypeContext> _useWstringHist;
};
Expand All @@ -84,7 +87,7 @@ namespace Slice
class DefaultFactoryVisitor final : public ParserVisitor
{
public:
DefaultFactoryVisitor(::IceInternal::Output&);
DefaultFactoryVisitor(IceInternal::Output&);
DefaultFactoryVisitor(const DefaultFactoryVisitor&) = delete;

bool visitUnitStart(const UnitPtr&) final;
Expand All @@ -93,7 +96,7 @@ namespace Slice
bool visitExceptionStart(const ExceptionPtr&) final;

private:
::IceInternal::Output& C;
IceInternal::Output& C;
/// when false, we need to generate a factory table init.
bool _factoryTableInitDone;
};
Expand All @@ -103,7 +106,7 @@ namespace Slice
class ProxyVisitor final : public ParserVisitor
{
public:
ProxyVisitor(::IceInternal::Output&, ::IceInternal::Output&, std::string);
ProxyVisitor(IceInternal::Output&, IceInternal::Output&, std::string);
ProxyVisitor(const ProxyVisitor&) = delete;

bool visitModuleStart(const ModulePtr&) final;
Expand All @@ -118,8 +121,8 @@ namespace Slice
const std::string& prefix,
const std::vector<std::string>& outgoingAsyncParams);

::IceInternal::Output& H;
::IceInternal::Output& C;
IceInternal::Output& H;
IceInternal::Output& C;

std::string _dllExport;
TypeContext _useWstring;
Expand All @@ -130,7 +133,7 @@ namespace Slice
class DataDefVisitor final : public ParserVisitor
{
public:
DataDefVisitor(::IceInternal::Output&, ::IceInternal::Output&, const std::string&);
DataDefVisitor(IceInternal::Output&, IceInternal::Output&, const std::string&);
DataDefVisitor(const DataDefVisitor&) = delete;

bool visitModuleStart(const ModulePtr&) final;
Expand All @@ -150,8 +153,8 @@ namespace Slice

void printFields(const DataMemberList& fields, bool firstField);

::IceInternal::Output& H;
::IceInternal::Output& C;
IceInternal::Output& H;
IceInternal::Output& C;

std::string _dllExport;
std::string _dllClassExport;
Expand All @@ -164,7 +167,7 @@ namespace Slice
class InterfaceVisitor final : public ParserVisitor
{
public:
InterfaceVisitor(::IceInternal::Output&, ::IceInternal::Output&, std::string);
InterfaceVisitor(IceInternal::Output&, IceInternal::Output&, std::string);
InterfaceVisitor(const InterfaceVisitor&) = delete;

bool visitModuleStart(const ModulePtr&) final;
Expand All @@ -174,8 +177,8 @@ namespace Slice
void visitOperation(const OperationPtr&) final;

private:
::IceInternal::Output& H;
::IceInternal::Output& C;
IceInternal::Output& H;
IceInternal::Output& C;

std::string _dllExport;
TypeContext _useWstring;
Expand All @@ -186,7 +189,7 @@ namespace Slice
class StreamVisitor final : public ParserVisitor
{
public:
StreamVisitor(::IceInternal::Output&);
StreamVisitor(IceInternal::Output&);
StreamVisitor(const StreamVisitor&) = delete;

bool visitModuleStart(const ModulePtr&) final;
Expand All @@ -195,7 +198,7 @@ namespace Slice
void visitEnum(const EnumPtr&) final;

private:
::IceInternal::Output& H;
IceInternal::Output& H;
};

static void validateMetadata(const UnitPtr&);
Expand Down
37 changes: 32 additions & 5 deletions cpp/test/Slice/print/AllTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ Test::MyCustomPrintException::ice_print(ostream& os) const
os << "Custom print: " << message;
}

ostream&
Test::operator<<(ostream& os, Fruit value)
{
os << (value == Fruit::Apple ? "yum" : "no thanks");
return os;
}

template<class T>
void
testPrint(const T& value, const string& expected, std::function<void(ostream&)> manip = nullptr)
Expand All @@ -45,21 +52,39 @@ testPrint(const T& value, const string& expected, std::function<void(ostream&)>
}
}

void
testEnum()
{
cout << "testing enum... " << flush;
testPrint(FlagColor::Red, "Red");
testPrint(FlagColor::OldGloryBlue, "OldGloryBlue");
testPrint(static_cast<FlagColor>(10), "10");
cout << "ok" << endl;
}

void
testCustomPrintEnum()
{
cout << "testing enum with custom print... " << flush;
testPrint(Fruit::Apple, "yum");
cout << "ok" << endl;
}

void
testSimpleStruct()
{
cout << "testing basic types... " << flush;
SimpleStruct simpleStruct{true, 199, 201, 150000, -100000000, 3.14f, 152853.5047l, "hello", FlagColor::Blue};
SimpleStruct simpleStruct{true, 199, 201, 150000, -10000000, 3.14f, 152853.5047l, "hello", FlagColor::OldGloryBlue};
testPrint(
simpleStruct,
"Test::SimpleStruct{myBool = true, myByte = 199, myShort = 201, myInt = 150000, myLong = -100000000, myFloat = "
"3.14, myDouble = 152854, myString = hello, myEnum = 2}");
"Test::SimpleStruct{myBool = true, myByte = 199, myShort = 201, myInt = 150000, myLong = -10000000, myFloat = "
"3.14, myDouble = 152854, myString = hello, myEnum = OldGloryBlue}");

// Increasing the floating-point precision to 9 digits changes the output (C++ default is 6).
testPrint(
simpleStruct,
"Test::SimpleStruct{myBool = true, myByte = 199, myShort = 201, myInt = 150000, myLong = -100000000, myFloat = "
"3.1400001, myDouble = 152853.505, myString = hello, myEnum = 2}",
"Test::SimpleStruct{myBool = true, myByte = 199, myShort = 201, myInt = 150000, myLong = -10000000, myFloat = "
"3.1400001, myDouble = 152853.505, myString = hello, myEnum = OldGloryBlue}",
[](ostream& os) { os << setprecision(9); });
cout << "ok" << endl;
}
Expand Down Expand Up @@ -181,6 +206,8 @@ void
allTests(Test::TestHelper* helper)
{
Ice::CommunicatorPtr communicator = helper->communicator();
testEnum();
testCustomPrintEnum();
testSimpleStruct();
testByteBoolStruct();
testCustomStruct();
Expand Down
5 changes: 4 additions & 1 deletion cpp/test/Slice/print/Test.ice
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@

module Test
{
enum FlagColor { Red, White, Blue }
enum FlagColor { Red, White, ["cpp:identifier:OldGloryBlue"] Blue }

["cpp:custom-print"]
enum Fruit { Apple, Banana, Cucumber }

struct SimpleStruct
{
Expand Down
3 changes: 3 additions & 0 deletions slice/DataStorm/Sample.ice
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright (c) ZeroC, Inc.

#pragma once

[["cpp:dll-export:DATASTORM_API"]]

module DataStorm
{
/// The sample event matches the operation used by the DataWriter to update the data element. It also provides
Expand Down

0 comments on commit bdf764b

Please sign in to comment.