Skip to content

Commit

Permalink
Add a scope concept to the DSL words (#156)
Browse files Browse the repository at this point in the history
Adds a new concept called `scope` to the NUClear DSL.
This concept can be used to add words which will be able to know when
they are running within a specific reaction.

For example, if you want to have a DSL word that modifies how events are
emitted within that task you can use this to track when the current task
is one with that word.
Then when another system needs to know if it is running in that context
it can call the in_scope function.
  • Loading branch information
TrentHouliston authored Oct 26, 2024
1 parent 12fab9a commit 3350a93
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 41 deletions.
8 changes: 8 additions & 0 deletions src/Reactor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ namespace dsl {

struct MainThread;

template <typename T>
struct TaskScope;

template <typename>
struct Network;

Expand Down Expand Up @@ -225,6 +228,10 @@ class Reactor {
/// @copydoc dsl::word::MainThread
using MainThread = dsl::word::MainThread;

/// @copydoc dsl::word::TaskScope
template <typename T>
using TaskScope = dsl::word::TaskScope<T>;

/// @copydoc dsl::word::Startup
using Startup = dsl::word::Startup;

Expand Down Expand Up @@ -474,6 +481,7 @@ class Reactor {
#include "dsl/word/Startup.hpp"
#include "dsl/word/Sync.hpp"
#include "dsl/word/TCP.hpp"
#include "dsl/word/TaskScope.hpp"
#include "dsl/word/Trigger.hpp"
#include "dsl/word/UDP.hpp"
#include "dsl/word/Watchdog.hpp"
Expand Down
10 changes: 7 additions & 3 deletions src/dsl/Fusion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
#include "fusion/GroupFusion.hpp"
#include "fusion/InlineFusion.hpp"
#include "fusion/PoolFusion.hpp"
#include "fusion/PostconditionFusion.hpp"
#include "fusion/PostRunFusion.hpp"
#include "fusion/PreRunFusion.hpp"
#include "fusion/PreconditionFusion.hpp"
#include "fusion/PriorityFusion.hpp"
#include "fusion/ScopeFusion.hpp"

namespace NUClear {
namespace dsl {
Expand All @@ -43,10 +45,12 @@ namespace dsl {
, fusion::GetFusion<Words...>
, fusion::GroupFusion<Words...>
, fusion::InlineFusion<Words...>
, fusion::PoolFusion<Words...>
, fusion::PostRunFusion<Words...>
, fusion::PreRunFusion<Words...>
, fusion::PreconditionFusion<Words...>
, fusion::PriorityFusion<Words...>
, fusion::PoolFusion<Words...>
, fusion::PostconditionFusion<Words...> {};
, fusion::ScopeFusion<Words...> {};

} // namespace dsl
} // namespace NUClear
Expand Down
27 changes: 19 additions & 8 deletions src/dsl/Parse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,25 +57,36 @@ namespace dsl {
Parse<Sentence...>>(task);
}

static bool precondition(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_precondition<DSL>::value, DSL, fusion::NoOp>::template precondition<
static std::shared_ptr<const util::ThreadPoolDescriptor> pool(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_pool<DSL>::value, DSL, fusion::NoOp>::template pool<
Parse<Sentence...>>(task);
}

static int priority(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_priority<DSL>::value, DSL, fusion::NoOp>::template priority<
static void post_run(threading::ReactionTask& task) {
std::conditional_t<fusion::has_post_run<DSL>::value, DSL, fusion::NoOp>::template post_run<
Parse<Sentence...>>(task);
}
static void pre_run(threading::ReactionTask& task) {
std::conditional_t<fusion::has_pre_run<DSL>::value, DSL, fusion::NoOp>::template pre_run<
Parse<Sentence...>>(task);
}

static std::shared_ptr<const util::ThreadPoolDescriptor> pool(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_pool<DSL>::value, DSL, fusion::NoOp>::template pool<
static bool precondition(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_precondition<DSL>::value, DSL, fusion::NoOp>::template precondition<
Parse<Sentence...>>(task);
}

static void postcondition(threading::ReactionTask& task) {
std::conditional_t<fusion::has_postcondition<DSL>::value, DSL, fusion::NoOp>::template postcondition<
static int priority(threading::ReactionTask& task) {
return std::conditional_t<fusion::has_priority<DSL>::value, DSL, fusion::NoOp>::template priority<
Parse<Sentence...>>(task);
}

static auto scope(threading::ReactionTask& task)
-> decltype(std::conditional_t<fusion::has_scope<DSL>::value, DSL, fusion::NoOp>::template scope<
Parse<Sentence...>>(task)) {
return std::conditional_t<fusion::has_scope<DSL>::value, DSL, fusion::NoOp>::template scope<Parse<Sentence...>>(
task);
}
};

} // namespace dsl
Expand Down
20 changes: 17 additions & 3 deletions src/dsl/fusion/NoOp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ namespace dsl {
return true;
}

template <typename DSL>
static void post_run(const threading::ReactionTask& /*task*/) {
// Empty as this is a no-op placeholder
}

template <typename DSL>
static void pre_run(const threading::ReactionTask& /*task*/) {
// Empty as this is a no-op placeholder
}

template <typename DSL>
static int priority(const threading::ReactionTask& /*task*/) {
return word::Priority::NORMAL::value;
Expand All @@ -80,8 +90,8 @@ namespace dsl {
}

template <typename DSL>
static void postcondition(const threading::ReactionTask& /*task*/) {
// Empty as this is a no-op placeholder
static std::tuple<> scope(const threading::ReactionTask& /*task*/) {
return {};
}
};

Expand All @@ -102,11 +112,15 @@ namespace dsl {

static bool precondition(threading::ReactionTask&);

static void post_run(threading::ReactionTask&);

static void pre_run(threading::ReactionTask&);

static int priority(threading::ReactionTask&);

static std::shared_ptr<const util::ThreadPoolDescriptor> pool(threading::ReactionTask&);

static void postcondition(threading::ReactionTask&);
static std::tuple<> scope(threading::ReactionTask&);
};

} // namespace fusion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef NUCLEAR_DSL_FUSION_POSTCONDITION_FUSION_HPP
#define NUCLEAR_DSL_FUSION_POSTCONDITION_FUSION_HPP
#ifndef NUCLEAR_DSL_FUSION_POST_RUN_FUSION_HPP
#define NUCLEAR_DSL_FUSION_POST_RUN_FUSION_HPP

#include "../../threading/ReactionTask.hpp"
#include "../operation/DSLProxy.hpp"
Expand All @@ -32,45 +32,45 @@ namespace NUClear {
namespace dsl {
namespace fusion {

/// Make a SFINAE type to check if a word has a postcondition method
HAS_NUCLEAR_DSL_METHOD(postcondition);
/// Make a SFINAE type to check if a word has a post_run method
HAS_NUCLEAR_DSL_METHOD(post_run);

// Default case where there are no postcondition words
// Default case where there are no post_run words
template <typename Words>
struct PostconditionFuser {};
struct PostRunFuser {};

// Case where there is only a single word remaining
template <typename Word>
struct PostconditionFuser<std::tuple<Word>> {
struct PostRunFuser<std::tuple<Word>> {

template <typename DSL>
static void postcondition(threading::ReactionTask& task) {
static void post_run(threading::ReactionTask& task) {

// Run our remaining postcondition
Word::template postcondition<DSL>(task);
// Run our remaining post_run
Word::template post_run<DSL>(task);
}
};

// Case where there is more 2 more more words remaining
template <typename Word1, typename Word2, typename... WordN>
struct PostconditionFuser<std::tuple<Word1, Word2, WordN...>> {
struct PostRunFuser<std::tuple<Word1, Word2, WordN...>> {

template <typename DSL>
static void postcondition(threading::ReactionTask& task) {
static void post_run(threading::ReactionTask& task) {

// Run our postcondition
Word1::template postcondition<DSL>(task);
// Run our post_run
Word1::template post_run<DSL>(task);

// Run the rest of our postconditions
PostconditionFuser<std::tuple<Word2, WordN...>>::template postcondition<DSL>(task);
// Run the rest of our post_runs
PostRunFuser<std::tuple<Word2, WordN...>>::template post_run<DSL>(task);
}
};

template <typename Word1, typename... WordN>
struct PostconditionFusion : PostconditionFuser<FindWords<has_postcondition, Word1, WordN...>> {};
struct PostRunFusion : PostRunFuser<FindWords<has_post_run, Word1, WordN...>> {};

} // namespace fusion
} // namespace dsl
} // namespace NUClear

#endif // NUCLEAR_DSL_FUSION_POSTCONDITION_FUSION_HPP
#endif // NUCLEAR_DSL_FUSION_POST_RUN_FUSION_HPP
76 changes: 76 additions & 0 deletions src/dsl/fusion/PreRunFusion.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* MIT License
*
* Copyright (c) 2024 NUClear Contributors
*
* This file is part of the NUClear codebase.
* See https://github.com/Fastcode/NUClear for further info.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef NUCLEAR_DSL_FUSION_PRE_RUN_FUSION_HPP
#define NUCLEAR_DSL_FUSION_PRE_RUN_FUSION_HPP

#include "../../threading/ReactionTask.hpp"
#include "../operation/DSLProxy.hpp"
#include "FindWords.hpp"
#include "has_nuclear_dsl_method.hpp"

namespace NUClear {
namespace dsl {
namespace fusion {

/// Make a SFINAE type to check if a word has a pre_run method
HAS_NUCLEAR_DSL_METHOD(pre_run);

// Default case where there are no pre_run words
template <typename Words>
struct PreRunFuser {};

// Case where there is only a single word remaining
template <typename Word>
struct PreRunFuser<std::tuple<Word>> {

template <typename DSL>
static void pre_run(threading::ReactionTask& task) {

// Run our remaining pre_run
Word::template pre_run<DSL>(task);
}
};

// Case where there is more 2 more more words remaining
template <typename Word1, typename Word2, typename... WordN>
struct PreRunFuser<std::tuple<Word1, Word2, WordN...>> {

template <typename DSL>
static void pre_run(threading::ReactionTask& task) {

// Run our pre_run
Word1::template pre_run<DSL>(task);

// Run the rest of our pre_runs
PreRunFuser<std::tuple<Word2, WordN...>>::template pre_run<DSL>(task);
}
};

template <typename Word1, typename... WordN>
struct PreRunFusion : PreRunFuser<FindWords<has_pre_run, Word1, WordN...>> {};

} // namespace fusion
} // namespace dsl
} // namespace NUClear

#endif // NUCLEAR_DSL_FUSION_PRE_RUN_FUSION_HPP
84 changes: 84 additions & 0 deletions src/dsl/fusion/ScopeFusion.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* MIT License
*
* Copyright (c) 2024 NUClear Contributors
*
* This file is part of the NUClear codebase.
* See https://github.com/Fastcode/NUClear for further info.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef NUCLEAR_DSL_FUSION_SCOPE_FUSION_HPP
#define NUCLEAR_DSL_FUSION_SCOPE_FUSION_HPP

#include "../../threading/Reaction.hpp"
#include "../../util/tuplify.hpp"
#include "../operation/DSLProxy.hpp"
#include "FindWords.hpp"
#include "has_nuclear_dsl_method.hpp"

namespace NUClear {
namespace dsl {
namespace fusion {

/// Make a SFINAE type to check if a word has a scope method
HAS_NUCLEAR_DSL_METHOD(scope);

/**
* This is our Function Fusion wrapper class that allows it to call scope functions.
*
* @tparam Function the scope function that we are wrapping for
* @tparam DSL the DSL that we pass to our scope function
*/
template <typename Function, typename DSL>
struct ScopeCaller {
static auto call(threading::ReactionTask& task) -> decltype(Function::template scope<DSL>(task)) {
return Function::template scope<DSL>(task);
}
};

// Default case where there are no scope words
template <typename Words>
struct ScopeFuser {};

// Case where there is at least one get word
template <typename Word1, typename... WordN>
struct ScopeFuser<std::tuple<Word1, WordN...>> {

template <typename DSL, typename U = Word1>
static auto scope(threading::ReactionTask& task)
-> decltype(util::FunctionFusion<std::tuple<Word1, WordN...>,
decltype(std::forward_as_tuple(task)),
ScopeCaller,
std::tuple<DSL>,
1>::call(task)) {

// Perform our function fusion
return util::FunctionFusion<std::tuple<Word1, WordN...>,
decltype(std::forward_as_tuple(task)),
ScopeCaller,
std::tuple<DSL>,
1>::call(task);
}
};

template <typename Word1, typename... WordN>
struct ScopeFusion : ScopeFuser<FindWords<has_scope, Word1, WordN...>> {};

} // namespace fusion
} // namespace dsl
} // namespace NUClear

#endif // NUCLEAR_DSL_FUSION_SCOPE_FUSION_HPP
2 changes: 1 addition & 1 deletion src/dsl/word/Always.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ namespace dsl {
}

template <typename DSL>
static void postcondition(threading::ReactionTask& task) {
static void post_run(threading::ReactionTask& task) {
// Get a task for the always reaction and submit it to the scheduler
PowerPlant::powerplant->submit(task.parent->get_task());
}
Expand Down
2 changes: 1 addition & 1 deletion src/dsl/word/IO.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ namespace dsl {
}

template <typename DSL>
static void postcondition(threading::ReactionTask& task) {
static void post_run(threading::ReactionTask& task) {
task.parent->reactor.emit<emit::Inline>(std::make_unique<IOFinished>(task.parent->id));
}
};
Expand Down
Loading

0 comments on commit 3350a93

Please sign in to comment.