Commit e2d30a85 authored by Dmytro Stechenko's avatar Dmytro Stechenko Committed by Facebook GitHub Bot

Add Badge pattern to lang utilities

Summary:
Nifty little trick to abstract over friend classes.
See: https://awesomekling.github.io/Serenity-C++-patterns-The-Badge/.

Reviewed By: yfeldblum

Differential Revision: D31881896

fbshipit-source-id: 651eaa10336909973478c23dac1de548adb51f6f
parent a00c6e30
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#pragma once
#if __has_include(<variant>)
#include <variant>
#endif
namespace folly {
/**
* Badge pattern allows us to abstract over friend classes and make friend
* feature more scoped. Using this simple technique we can specify a badge tag
* on specific functions we want to gate for particular caller contexts (badge
* holders). Badge can only be constructed by the specified badge holder binding
* the tagged functions to that call site.
*
* Example:
* class ProtectedClass: {
* ...
* public:
* ...
* static void func(folly::badge<FriendClass>);
* };
*
* void FriendClass::callProtectedFunc() {
* ProtectedClass::func({}); // compiles
* }
* void OtherClass::callProtectedFunc() {
* ProtectedClass::func({}); // does not compile!
* }
*
* See: https://awesomekling.github.io/Serenity-C++-patterns-The-Badge/
* Author: Andreas Kling (https://github.com/awesomekling)
*
*/
template <typename Holder>
class badge {
friend Holder;
badge() {}
};
#if __has_include(<variant>)
/**
* For cases when multiple badge holders are required to call a function we can
* use std::variant<...> over individual badges.
*
* Example:
* class ProtectedClass: {
* public:
* static void func(folly::badges<FriendClass, OtherFriendClass>);
* };
*
* void FriendClass::callProtectedFunc() {
* ProtectedClass::func(folly::badge<FriendClass>{}); // compiles
* }
* void OtherFriendClass::callProtectedFunc() {
* ProtectedClass::func(folly::badge<OtherFriendClass>{}); // compiles
* }
*/
template <typename... Holders>
using badges = std::variant<badge<Holders>...>;
#endif
} // namespace folly
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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.
*/
#include <type_traits>
#include <folly/lang/Badge.h>
#include <folly/portability/GTest.h>
namespace {
class FriendClass;
class OtherFriendClass;
using SingleBadge = folly::badge<FriendClass>;
using OtherSingleBadge = folly::badge<OtherFriendClass>;
using MultipleBadges = folly::badges<FriendClass, OtherFriendClass>;
class ProtectedClass {
public:
static void single(SingleBadge) {}
static void multiple(MultipleBadges) {}
};
class FriendClass {
public:
static void single() { ProtectedClass::single({}); }
static void multiple() { ProtectedClass::multiple(SingleBadge{}); }
};
class OtherFriendClass {
public:
static void multiple() { ProtectedClass::multiple(OtherSingleBadge{}); }
};
} // namespace
TEST(BadgeTest, test_single_badge) {
// check a badge cannot be constructed outside of the context
EXPECT_FALSE(std::is_default_constructible_v<SingleBadge>);
// check a badge can be constructed from the allowed context
FriendClass::single();
}
TEST(BadgeTest, test_multiple_badges) {
// check a badge cannot be constructed outside of the context
EXPECT_FALSE(std::is_default_constructible_v<SingleBadge>);
EXPECT_FALSE(std::is_default_constructible_v<OtherSingleBadge>);
EXPECT_FALSE(std::is_default_constructible_v<MultipleBadges>);
// check a badge can be constructed from the allowed context
FriendClass::multiple();
OtherFriendClass::multiple();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment