2025-09-29 13:35:48 -04:00
# pragma once
# include "gridfire/trigger/trigger_abstract.h"
# include "gridfire/trigger/trigger_result.h"
# include <string>
# include <vector>
# include <memory>
2025-10-08 11:17:35 -04:00
# include <stdexcept>
2025-09-29 13:35:48 -04:00
namespace gridfire : : trigger {
2025-10-08 11:17:35 -04:00
/**
* @ file trigger_logical . h
* @ brief Combinators for composing triggers with boolean logic ( AND / OR / NOT / EveryNth ) .
*
* These templates wrap any Trigger < Context > and provide convenient composition . They also
* maintain simple hit / miss counters and implement short - circuit logic in check ( ) and why ( ) .
*/
2025-09-29 13:35:48 -04:00
template < typename TriggerContextStruct >
class LogicalTrigger : public Trigger < TriggerContextStruct > { } ;
2025-10-08 11:17:35 -04:00
/**
* @ class AndTrigger
* @ brief Logical conjunction of two triggers with short - circuit evaluation .
*
* check ( ctx ) returns A . check ( ctx ) & & B . check ( ctx ) . The why ( ctx ) explanation short - circuits
* if A is false , avoiding evaluation of B . update ( ctx ) calls update ( ) on both A and B .
*
* Counters ( mutable ) are incremented inside const check ( ) : m_hits on true ; m_misses on false ;
* m_updates on each update ( ) ; m_resets on reset ( ) .
*
* @ par Example
* @ code
* auto t = AndTrigger ( ctxA , ctxB ) ;
* if ( t . check ( ctx ) ) { ( void ) ctx ; }
* @ endcode
*/
2025-09-29 13:35:48 -04:00
template < typename TriggerContextStruct >
class AndTrigger final : public LogicalTrigger < TriggerContextStruct > {
public :
2025-10-08 11:17:35 -04:00
/**
* @ brief Construct AND from two triggers ( ownership transferred ) .
*/
2025-09-29 13:35:48 -04:00
AndTrigger ( std : : unique_ptr < Trigger < TriggerContextStruct > > A , std : : unique_ptr < Trigger < TriggerContextStruct > > B ) ;
~ AndTrigger ( ) override = default ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Evaluate A & & B ; increments hit / miss counters .
*/
2025-09-29 13:35:48 -04:00
bool check ( const TriggerContextStruct & ctx ) const override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Update both sub - triggers and increment update counter .
*/
2025-09-29 13:35:48 -04:00
void update ( const TriggerContextStruct & ctx ) override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Reset both sub - triggers and local counters .
*/
2025-09-29 13:35:48 -04:00
void reset ( ) override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Human - readable name .
*/
2025-09-29 13:35:48 -04:00
std : : string name ( ) const override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Structured explanation ; short - circuits on A = false .
*/
2025-09-29 13:35:48 -04:00
TriggerResult why ( const TriggerContextStruct & ctx ) const override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Description expression e . g . " (A) AND (B) " .
*/
2025-09-29 13:35:48 -04:00
std : : string describe ( ) const override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Number of true evaluations since last reset .
*/
2025-09-29 13:35:48 -04:00
size_t numTriggers ( ) const override ;
2025-10-08 11:17:35 -04:00
/**
* @ brief Number of false evaluations since last reset .
*/
2025-09-29 13:35:48 -04:00
size_t numMisses ( ) const override ;
private :
std : : unique_ptr < Trigger < TriggerContextStruct > > m_A ;
std : : unique_ptr < Trigger < TriggerContextStruct > > m_B ;
mutable size_t m_hits = 0 ;
mutable size_t m_misses = 0 ;
mutable size_t m_updates = 0 ;
mutable size_t m_resets = 0 ;
} ;
2025-10-08 11:17:35 -04:00
/**
* @ class OrTrigger
* @ brief Logical disjunction of two triggers with short - circuit evaluation .
*
* check ( ctx ) returns A . check ( ctx ) | | B . check ( ctx ) . why ( ctx ) returns early when A is true .
* update ( ctx ) calls update ( ) on both A and B . Counters behave as in AndTrigger .
*/
2025-09-29 13:35:48 -04:00
template < typename TriggerContextStruct >
class OrTrigger final : public LogicalTrigger < TriggerContextStruct > {
public :
OrTrigger ( std : : unique_ptr < Trigger < TriggerContextStruct > > A , std : : unique_ptr < Trigger < TriggerContextStruct > > B ) ;
~ OrTrigger ( ) override = default ;
bool check ( const TriggerContextStruct & ctx ) const override ;
void update ( const TriggerContextStruct & ctx ) override ;
void reset ( ) override ;
std : : string name ( ) const override ;
TriggerResult why ( const TriggerContextStruct & ctx ) const override ;
std : : string describe ( ) const override ;
size_t numTriggers ( ) const override ;
size_t numMisses ( ) const override ;
private :
std : : unique_ptr < Trigger < TriggerContextStruct > > m_A ;
std : : unique_ptr < Trigger < TriggerContextStruct > > m_B ;
mutable size_t m_hits = 0 ;
mutable size_t m_misses = 0 ;
mutable size_t m_updates = 0 ;
mutable size_t m_resets = 0 ;
} ;
2025-10-08 11:17:35 -04:00
/**
* @ class NotTrigger
* @ brief Logical negation of a trigger .
*
* check ( ctx ) returns ! A . check ( ctx ) . why ( ctx ) explains the inverted condition . Counter
* semantics match the other logical triggers .
*/
2025-09-29 13:35:48 -04:00
template < typename TriggerContextStruct >
class NotTrigger final : public LogicalTrigger < TriggerContextStruct > {
public :
explicit NotTrigger ( std : : unique_ptr < Trigger < TriggerContextStruct > > A ) ;
~ NotTrigger ( ) override = default ;
bool check ( const TriggerContextStruct & ctx ) const override ;
void update ( const TriggerContextStruct & ctx ) override ;
void reset ( ) override ;
std : : string name ( ) const override ;
TriggerResult why ( const TriggerContextStruct & ctx ) const override ;
std : : string describe ( ) const override ;
size_t numTriggers ( ) const override ;
size_t numMisses ( ) const override ;
private :
std : : unique_ptr < Trigger < TriggerContextStruct > > m_A ;
mutable size_t m_hits = 0 ;
mutable size_t m_misses = 0 ;
mutable size_t m_updates = 0 ;
mutable size_t m_resets = 0 ;
} ;
2025-10-08 11:17:35 -04:00
/**
* @ class EveryNthTrigger
* @ brief Pass - through trigger that fires every Nth time its child trigger is true .
*
* On update ( ctx ) , increments an internal counter when A . check ( ctx ) is true . check ( ctx )
* returns true only when A . check ( ctx ) is true and the internal counter is a multiple of N .
*
* @ throws std : : invalid_argument When constructed with N = = 0.
*/
2025-09-29 13:35:48 -04:00
template < typename TriggerContextStruct >
class EveryNthTrigger final : public LogicalTrigger < TriggerContextStruct > {
public :
explicit EveryNthTrigger ( std : : unique_ptr < Trigger < TriggerContextStruct > > A , size_t N ) ;
~ EveryNthTrigger ( ) override = default ;
bool check ( const TriggerContextStruct & ctx ) const override ;
void update ( const TriggerContextStruct & ctx ) override ;
void reset ( ) override ;
std : : string name ( ) const override ;
TriggerResult why ( const TriggerContextStruct & ctx ) const override ;
std : : string describe ( ) const override ;
size_t numTriggers ( ) const override ;
size_t numMisses ( ) const override ;
private :
std : : unique_ptr < Trigger < TriggerContextStruct > > m_A ;
size_t m_N ;
mutable size_t m_counter = 0 ;
mutable size_t m_hits = 0 ;
mutable size_t m_misses = 0 ;
mutable size_t m_updates = 0 ;
mutable size_t m_resets = 0 ;
} ;
///////////////////////////////
// Templated Implementations //
///////////////////////////////
template < typename TriggerContextStruct >
AndTrigger < TriggerContextStruct > : : AndTrigger (
std : : unique_ptr < Trigger < TriggerContextStruct > > A ,
std : : unique_ptr < Trigger < TriggerContextStruct > > B
) : m_A ( std : : move ( A ) ) , m_B ( std : : move ( B ) ) { }
template < typename TriggerContextStruct >
bool AndTrigger < TriggerContextStruct > : : check ( const TriggerContextStruct & ctx ) const {
const bool valid = m_A - > check ( ctx ) & & m_B - > check ( ctx ) ;
if ( valid ) {
m_hits + + ;
} else {
m_misses + + ;
}
return valid ;
}
template < typename TriggerContextStruct >
void AndTrigger < TriggerContextStruct > : : update ( const TriggerContextStruct & ctx ) {
m_A - > update ( ctx ) ;
m_B - > update ( ctx ) ;
m_updates + + ;
}
template < typename TriggerContextStruct >
void AndTrigger < TriggerContextStruct > : : reset ( ) {
m_A - > reset ( ) ;
m_B - > reset ( ) ;
m_resets + + ;
m_hits = 0 ;
m_misses = 0 ;
m_updates = 0 ;
}
template < typename TriggerContextStruct >
std : : string AndTrigger < TriggerContextStruct > : : name ( ) const {
return " AND Trigger " ;
}
template < typename TriggerContextStruct >
TriggerResult AndTrigger < TriggerContextStruct > : : why ( const TriggerContextStruct & ctx ) const {
TriggerResult result ;
result . name = name ( ) ;
TriggerResult A_result = m_A - > why ( ctx ) ;
result . causes . push_back ( A_result ) ;
if ( ! A_result . value ) {
// Short Circuit
result . value = false ;
result . description = " Failed because A ( " + A_result . name + " ) is false. " ;
return result ;
}
TriggerResult B_result = m_B - > why ( ctx ) ;
result . causes . push_back ( B_result ) ;
if ( ! B_result . value ) {
result . value = false ;
result . description = " Failed because B ( " + B_result . name + " ) is false. " ;
return result ;
}
result . value = true ;
result . description = " Succeeded because both A ( " + A_result . name + " ) and B ( " + B_result . description + " ) are true. " ;
return result ;
}
template < typename TriggerContextStruct >
std : : string AndTrigger < TriggerContextStruct > : : describe ( ) const {
return " ( " + m_A - > describe ( ) + " ) AND ( " + m_B - > describe ( ) + " ) " ;
}
template < typename TriggerContextStruct >
size_t AndTrigger < TriggerContextStruct > : : numTriggers ( ) const {
return m_hits ;
}
template < typename TriggerContextStruct >
size_t AndTrigger < TriggerContextStruct > : : numMisses ( ) const {
return m_misses ;
}
template < typename TriggerContextStruct >
OrTrigger < TriggerContextStruct > : : OrTrigger (
std : : unique_ptr < Trigger < TriggerContextStruct > > A ,
std : : unique_ptr < Trigger < TriggerContextStruct > > B
) : m_A ( std : : move ( A ) ) , m_B ( std : : move ( B ) ) { }
template < typename TriggerContextStruct >
bool OrTrigger < TriggerContextStruct > : : check ( const TriggerContextStruct & ctx ) const {
const bool valid = m_A - > check ( ctx ) | | m_B - > check ( ctx ) ;
if ( valid ) {
m_hits + + ;
} else {
m_misses + + ;
}
return valid ;
}
template < typename TriggerContextStruct >
void OrTrigger < TriggerContextStruct > : : update ( const TriggerContextStruct & ctx ) {
m_A - > update ( ctx ) ;
m_B - > update ( ctx ) ;
m_updates + + ;
}
template < typename TriggerContextStruct >
void OrTrigger < TriggerContextStruct > : : reset ( ) {
m_A - > reset ( ) ;
m_B - > reset ( ) ;
m_resets + + ;
m_hits = 0 ;
m_misses = 0 ;
m_updates = 0 ;
}
template < typename TriggerContextStruct >
std : : string OrTrigger < TriggerContextStruct > : : name ( ) const {
return " OR Trigger " ;
}
template < typename TriggerContextStruct >
TriggerResult OrTrigger < TriggerContextStruct > : : why ( const TriggerContextStruct & ctx ) const {
TriggerResult result ;
result . name = name ( ) ;
TriggerResult A_result = m_A - > why ( ctx ) ;
result . causes . push_back ( A_result ) ;
if ( A_result . value ) {
// Short Circuit
result . value = true ;
result . description = " Succeeded because A ( " + A_result . name + " ) is true. " ;
return result ;
}
TriggerResult B_result = m_B - > why ( ctx ) ;
result . causes . push_back ( B_result ) ;
if ( B_result . value ) {
result . value = true ;
result . description = " Succeeded because B ( " + B_result . name + " ) is true. " ;
return result ;
}
result . value = false ;
result . description = " Failed because both A ( " + A_result . name + " ) and B ( " + B_result . name + " ) are false. " ;
return result ;
}
template < typename TriggerContextStruct >
std : : string OrTrigger < TriggerContextStruct > : : describe ( ) const {
return " ( " + m_A - > describe ( ) + " ) OR ( " + m_B - > describe ( ) + " ) " ;
}
template < typename TriggerContextStruct >
size_t OrTrigger < TriggerContextStruct > : : numTriggers ( ) const {
return m_hits ;
}
template < typename TriggerContextStruct >
size_t OrTrigger < TriggerContextStruct > : : numMisses ( ) const {
return m_misses ;
}
template < typename TriggerContextStruct >
NotTrigger < TriggerContextStruct > : : NotTrigger (
std : : unique_ptr < Trigger < TriggerContextStruct > > A
) : m_A ( std : : move ( A ) ) { }
template < typename TriggerContextStruct >
bool NotTrigger < TriggerContextStruct > : : check ( const TriggerContextStruct & ctx ) const {
const bool valid = ! m_A - > check ( ctx ) ;
if ( valid ) {
m_hits + + ;
} else {
m_misses + + ;
}
return valid ;
}
template < typename TriggerContextStruct >
void NotTrigger < TriggerContextStruct > : : update ( const TriggerContextStruct & ctx ) {
m_A - > update ( ctx ) ;
m_updates + + ;
}
template < typename TriggerContextStruct >
void NotTrigger < TriggerContextStruct > : : reset ( ) {
m_A - > reset ( ) ;
m_resets + + ;
m_hits = 0 ;
m_misses = 0 ;
m_updates = 0 ;
}
template < typename TriggerContextStruct >
std : : string NotTrigger < TriggerContextStruct > : : name ( ) const {
return " NOT Trigger " ;
}
template < typename TriggerContextStruct >
TriggerResult NotTrigger < TriggerContextStruct > : : why ( const TriggerContextStruct & ctx ) const {
TriggerResult result ;
result . name = name ( ) ;
TriggerResult A_result = m_A - > why ( ctx ) ;
result . causes . push_back ( A_result ) ;
if ( A_result . value ) {
result . value = false ;
result . description = " Failed because A ( " + A_result . name + " ) is true. " ;
return result ;
}
result . value = true ;
result . description = " Succeeded because A ( " + A_result . name + " ) is false. " ;
return result ;
}
template < typename TriggerContextStruct >
std : : string NotTrigger < TriggerContextStruct > : : describe ( ) const {
return " NOT ( " + m_A - > describe ( ) + " ) " ;
}
template < typename TriggerContextStruct >
size_t NotTrigger < TriggerContextStruct > : : numTriggers ( ) const {
return m_hits ;
}
template < typename TriggerContextStruct >
size_t NotTrigger < TriggerContextStruct > : : numMisses ( ) const {
return m_misses ;
}
template < typename TriggerContextStruct >
EveryNthTrigger < TriggerContextStruct > : : EveryNthTrigger ( std : : unique_ptr < Trigger < TriggerContextStruct > > A , const size_t N ) : m_A ( std : : move ( A ) ) , m_N ( N ) {
if ( N = = 0 ) {
throw std : : invalid_argument ( " N must be greater than 0. " ) ;
}
}
template < typename TriggerContextStruct >
bool EveryNthTrigger < TriggerContextStruct > : : check ( const TriggerContextStruct & ctx ) const
{
if ( m_A - > check ( ctx ) & & ( m_counter % m_N = = 0 ) ) {
m_hits + + ;
return true ;
}
m_misses + + ;
return false ;
}
template < typename TriggerContextStruct >
void EveryNthTrigger < TriggerContextStruct > : : update ( const TriggerContextStruct & ctx ) {
if ( m_A - > check ( ctx ) ) {
m_counter + + ;
}
m_A - > update ( ctx ) ;
m_updates + + ;
}
template < typename TriggerContextStruct >
void EveryNthTrigger < TriggerContextStruct > : : reset ( ) {
m_A - > reset ( ) ;
m_resets + + ;
m_counter = 0 ;
m_hits = 0 ;
m_misses = 0 ;
m_updates = 0 ;
}
template < typename TriggerContextStruct >
std : : string EveryNthTrigger < TriggerContextStruct > : : name ( ) const {
return " Every Nth Trigger " ;
}
template < typename TriggerContextStruct >
TriggerResult EveryNthTrigger < TriggerContextStruct > : : why ( const TriggerContextStruct & ctx ) const {
TriggerResult result ;
result . name = name ( ) ;
TriggerResult A_result = m_A - > why ( ctx ) ;
result . causes . push_back ( A_result ) ;
if ( ! A_result . value ) {
result . value = false ;
result . description = " Failed because A ( " + A_result . name + " ) is false. " ;
return result ;
}
if ( m_counter % m_N = = 0 ) {
result . value = true ;
result . description = " Succeeded because A ( " + A_result . name + " ) is true and the counter ( " + std : : to_string ( m_counter ) + " ) is a multiple of N ( " + std : : to_string ( m_N ) + " ). " ;
return result ;
}
result . value = false ;
result . description = " Failed because the counter ( " + std : : to_string ( m_counter ) + " ) is not a multiple of N ( " + std : : to_string ( m_N ) + " ). " ;
return result ;
}
template < typename TriggerContextStruct >
std : : string EveryNthTrigger < TriggerContextStruct > : : describe ( ) const {
return " Every " + std : : to_string ( m_N ) + " th ( " + m_A - > describe ( ) + " ) " ;
}
template < typename TriggerContextStruct >
size_t EveryNthTrigger < TriggerContextStruct > : : numTriggers ( ) const {
return m_hits ;
}
template < typename TriggerContextStruct >
size_t EveryNthTrigger < TriggerContextStruct > : : numMisses ( ) const {
return m_misses ;
}
}