Use x-macros for action args too (#11859)

This adds x-macros for each of the actions, greatly reducing the amount of boilerplate needed for each action args. 

Originally, I wanted to do more with this, but I think the x-macros we've discovered properly treads the line of ease-of-use and native c++ support, with how much it'll do for us

* [x] Closes #3475
* [x] Sure enough, the tests still pass.
* [ ] mmmmmmm  ![image](https://user-images.githubusercontent.com/18356694/144319133-329ee7ef-4aa7-4769-b11b-6e4994075dd0.png)
This commit is contained in:
Mike Griese 2021-12-06 14:10:12 -06:00 committed by GitHub
parent 29e6235151
commit 81d9297537
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 348 additions and 1417 deletions

View File

@ -23,9 +23,11 @@ Fitt
formattings
ftp
fvar
gcc
geeksforgeeks
ghe
gje
godbolt
hostname
hostnames
hyperlink

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,160 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- ActionArgsMagic.h
Abstract:
- Contains helpers for x-macro defining all our action args. This doesn't
contain the actual x-macros themselves, but does contain the logic for
actually synthesizing an ActionArgs implementation.
- Most of the time, you'll only need ACTION_ARGS_STRUCT
- ACTION_ARG_BODY is for when you've got some other logic to add to the class.
Author(s):
- Mike Griese - December 2021
--*/
// MACRO HACKS
//
// We want to have code that looks like:
//
// FooArgs(const ParamOne& one, const ParamTwo& two) :
// _One{ one }, _Two{ two } {};
//
// However, if we just use the x-macro for this straight up, then the list will
// have a trailing comma at the end of it, and won't compile. So, we're creating
// this placeholder size-0 struct. It's going to be the last param for all the
// args' ctors. It'll have a default value, so no one will need to know about
// it. This will let us use the macro to populate the ctors as well.
struct InitListPlaceholder
{
};
//
// The complete ActionAndArgs definition. Each macro above ACTION_ARGS_STRUCT is
// some element of the class definition that will use the x-macro.
//
// You'll author a new arg by:
// 1: define a new x-macro above with all it's properties
// 2. Define the class with:
//
// ACTION_ARGS_STRUCT(MyFooArgs, MY_FOO_ARGS);
//
// In that macro, we'll use the passed-in macro (MY_FOO_ARGS) with each of these
// macros below, which will generate the various parts of the class body.
//
// Trying to make changes here? I'd recommend godbolt with the `-E` flag to gcc.
// That'll output the expanded macros, so you can see how it will all get
// expanded. Pretty critical for tracking down extraneous commas, etc.
// Property definitions, and JSON keys
#define DECLARE_ARGS(type, name, jsonKey, required, ...) \
static constexpr std::string_view name##Key{ jsonKey }; \
ACTION_ARG(type, name, ##__VA_ARGS__);
// Parameters to the non-default ctor
#define CTOR_PARAMS(type, name, jsonKey, required, ...) \
const type &name##Param,
// initializers in the ctor
#define CTOR_INIT(type, name, jsonKey, required, ...) \
_##name{ name##Param },
// check each property in the Equals() method. You'll note there's a stray
// `true` in the definition of Equals() below, that's to deal with trailing
// commas
#define EQUALS_ARGS(type, name, jsonKey, required, ...) \
&&(otherAsUs->_##name == _##name)
// JSON deserialization. If the parameter is required to pass any validation,
// add that as the `required` parameter here, as the body of a conditional
// EX: For the RESIZE_PANE_ARGS
// X(Model::ResizeDirection, ResizeDirection, "direction", args->ResizeDirection() == ResizeDirection::None, Model::ResizeDirection::None)
// the bit
// args->ResizeDirection() == ResizeDirection::None
// is used as the conditional for the validation here.
#define FROM_JSON_ARGS(type, name, jsonKey, required, ...) \
if (required) \
{ \
return { nullptr, { SettingsLoadWarnings::MissingRequiredParameter } }; \
} \
JsonUtils::GetValueForKey(json, jsonKey, args->_##name);
// JSON serialization
#define TO_JSON_ARGS(type, name, jsonKey, required, ...) \
JsonUtils::SetValueForKey(json, jsonKey, args->_##name);
// Copy each property in the Copy() method
#define COPY_ARGS(type, name, jsonKey, required, ...) \
copy->_##name = _##name;
// hash each property in Hash(). You'll note there's a stray `0` in the
// definition of Hash() below, that's to deal with trailing commas (or in this
// case, leading.)
#define HASH_ARGS(type, name, jsonKey, required, ...) \
, name()
// Use ACTION_ARGS_STRUCT when you've got no other customizing to do.
#define ACTION_ARGS_STRUCT(className, argsMacro) \
struct className : public className##T<className> \
{ \
ACTION_ARG_BODY(className, argsMacro) \
};
// Use ACTION_ARG_BODY when you've got some other methods to add to the args class.
// case in point:
// * NewTerminalArgs has a ToCommandline method it needs to additionally declare.
// * GlobalSummonArgs has the QuakeModeFromJson helper
#define ACTION_ARG_BODY(className, argsMacro) \
className() = default; \
className( \
argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \
argsMacro(CTOR_INIT) _placeholder{} {}; \
argsMacro(DECLARE_ARGS); \
\
private: \
InitListPlaceholder _placeholder; \
\
public: \
hstring GenerateName() const; \
bool Equals(const IActionArgs& other) \
{ \
auto otherAsUs = other.try_as<className>(); \
if (otherAsUs) \
{ \
return true argsMacro(EQUALS_ARGS); \
} \
return false; \
}; \
static FromJsonResult FromJson(const Json::Value& json) \
{ \
auto args = winrt::make_self<className>(); \
argsMacro(FROM_JSON_ARGS); \
return { *args, {} }; \
} \
static Json::Value ToJson(const IActionArgs& val) \
{ \
if (!val) \
{ \
return {}; \
} \
Json::Value json{ Json::ValueType::objectValue }; \
const auto args{ get_self<className>(val) }; \
argsMacro(TO_JSON_ARGS); \
return json; \
} \
IActionArgs Copy() const \
{ \
auto copy{ winrt::make_self<className>() }; \
argsMacro(COPY_ARGS); \
return *copy; \
} \
size_t Hash() const \
{ \
return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty( \
0 argsMacro(HASH_ARGS)); \
}

View File

@ -14,6 +14,7 @@
<Import Project="$(OpenConsoleDir)src\cppwinrt.build.pre.props" />
<!-- ========================= Headers ======================== -->
<ItemGroup>
<ClInclude Include="ActionArgsMagic.h" />
<ClInclude Include="VisualStudioGenerator.h" />
<ClInclude Include="DefaultTerminal.h">
<DependentUpon>DefaultTerminal.idl</DependentUpon>