Skip to content

Commit

Permalink
Merge pull request #375 from icyphy/cpp-scoping
Browse files Browse the repository at this point in the history
Properly implement scoping rules in the generated C++ code
  • Loading branch information
lhstrh authored Jul 6, 2021
2 parents 359ba35 + 9ea3bdd commit bd6ad65
Show file tree
Hide file tree
Showing 17 changed files with 320 additions and 101 deletions.
2 changes: 1 addition & 1 deletion example/Cpp/src/CarBrake/CarBrake.lf
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ reactor BrakePedal {
state thread: std::thread;

reaction(startup) -> pedal {=
this->thread = std::thread([this] () {
this->thread = std::thread([&] () {
// press the brake pedal roughly every second
while (true) {
std::this_thread::sleep_for(1005ms);
Expand Down
2 changes: 1 addition & 1 deletion example/Cpp/src/CarBrake/CarBrake2.lf
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ reactor BrakePedal {
state thread: std::thread;

reaction(startup) -> pedal {=
this->thread = std::thread([this] () {
this->thread = std::thread([&] () {
// press the brake pedal roughly every second
while (true) {
std::this_thread::sleep_for(1005ms);
Expand Down
2 changes: 1 addition & 1 deletion example/Cpp/src/ReflexGame/ReflexGame.lf
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ reactor GetUserInput {

reaction(startup) -> user_response {=
// Start the thread that listens for Enter or Return.
thread = std::thread([this] () {
thread = std::thread([&] () {
int c;
while(1) {
while((c = getchar()) != '\n') {
Expand Down
17 changes: 15 additions & 2 deletions org.lflang/src/lib/Cpp/lfutil.hh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
namespace lfutil {

template<class T>
void after_delay(reactor::Action<T>* action, reactor::Port<T>* port) {
void after_delay(reactor::Action<T>* action, const reactor::Port<T>* port) {
if constexpr(std::is_void<T>::value) {
action->schedule();
} else {
Expand All @@ -38,12 +38,25 @@ void after_delay(reactor::Action<T>* action, reactor::Port<T>* port) {
}

template<class T>
void after_forward(reactor::Action<T>* action, reactor::Port<T>* port) {
void after_forward(const reactor::Action<T>* action, reactor::Port<T>* port) {
if constexpr(std::is_void<T>::value) {
port->set();
} else {
port->set(std::move(action->get()));
}
}

class LFScope {
private:
reactor::Reactor* reactor;
public:
LFScope(reactor::Reactor* reactor) : reactor(reactor) {}

reactor::TimePoint get_physical_time() const { return reactor->get_physical_time(); }
reactor::TimePoint get_logical_time() const { return reactor->get_logical_time(); }
reactor::Duration get_elapsed_logical_time() const { return reactor->get_elapsed_logical_time(); }
reactor::Duration get_elapsed_physical_time() const { return reactor->get_elapsed_physical_time(); }
reactor::Environment* environment() const { return reactor->environment(); }
};

}
11 changes: 7 additions & 4 deletions org.lflang/src/org/lflang/generator/cpp/CppActionGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ import org.lflang.lf.Reactor
/** A C++ code generator for actions */
class CppActionGenerator(private val reactor: Reactor, private val errorReporter: ErrorReporter) {

private val Action.cppType get() = if (this.isLogical) "reactor::LogicalAction" else "reactor::PhysicalAction"
companion object {
val Action.cppType
get() = if (this.isLogical) "reactor::LogicalAction<$targetType>" else "reactor::PhysicalAction<$targetType>"

private val startupName = LfPackage.Literals.TRIGGER_REF__STARTUP.name
private val shutdownName = LfPackage.Literals.TRIGGER_REF__SHUTDOWN.name
val startupName: String = LfPackage.Literals.TRIGGER_REF__STARTUP.name
val shutdownName: String = LfPackage.Literals.TRIGGER_REF__SHUTDOWN.name
}

private fun generateDeclaration(action: Action) = "${action.cppType}<${action.targetType}> ${action.name};"
private fun generateDeclaration(action: Action) = "${action.cppType} ${action.name};"

private fun generateInitializer(action: Action) =
if (action.isLogical) generateLogicalInitializer(action) else initializePhysicalInitializer(action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class CppAssembleMethodGenerator(
}

private fun setDeadline(reaction: Reaction): String =
"${reaction.name}.set_deadline(${reaction.deadline.delay.toTime()}, [this]() { ${reaction.name}_deadline_handler(); });"
"${reaction.name}.set_deadline(${reaction.deadline.delay.toTime(true)}, [this]() { ${reaction.name}_deadline_handler(); });"

private fun assembleReaction(reaction: Reaction) = with(PrependOperator) {
"""
Expand Down
6 changes: 6 additions & 0 deletions org.lflang/src/org/lflang/generator/cpp/CppCmakeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ class CppCmakeGenerator(private val targetConfig: TargetConfig, private val file
|)
|target_link_libraries($S{LF_MAIN_TARGET} reactor-cpp)
|
|if(MSVC)
| target_compile_options($S{LF_MAIN_TARGET} PRIVATE /W4)
|else()
| target_compile_options($S{LF_MAIN_TARGET} PRIVATE -Wall -Wextra -pedantic)
|endif()
|
|install(TARGETS $S{LF_MAIN_TARGET}
| RUNTIME DESTINATION $S{CMAKE_INSTALL_BINDIR}
|)
Expand Down
83 changes: 62 additions & 21 deletions org.lflang/src/org/lflang/generator/cpp/CppConstructorGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class CppConstructorGenerator(
private val state: CppStateGenerator,
private val instances: CppInstanceGenerator,
private val timers: CppTimerGenerator,
private val actions: CppActionGenerator
private val actions: CppActionGenerator,
private val reactions: CppReactionGenerator,
) {

/**
Expand All @@ -45,38 +46,78 @@ class CppConstructorGenerator(
private val environmentOrContainer =
if (reactor.isMain) "reactor::Environment* environment" else "reactor::Reactor* container"

private fun signature(withDefaults: Boolean): String {
if (reactor.parameters.size > 0) {
val parameterArgs = with(CppParameterGenerator) {
if (withDefaults) reactor.parameters.map { "${it.constRefType} ${it.name} = ${it.defaultValue}" }
else reactor.parameters.map { "${it.constRefType} ${it.name}" }
/**
* Get a list of all parameters as they appear in the argument list of the constructors.
*
* @param withDefaults If true, then include default parameter values.
* @return a list of Strings containing all parameters to be used in the constructor signature
*/
private fun parameterArguments(withDefaults: Boolean) = with(CppParameterGenerator) {
if (withDefaults) reactor.parameters.map { "${it.constRefType} ${it.name} = ${it.defaultValue}" }
else reactor.parameters.map { "${it.constRefType} ${it.name}" }
}

private fun outerSignature(withDefaults: Boolean): String {
val parameterArgs = parameterArguments(withDefaults)
return if (parameterArgs.isEmpty())
"""${reactor.name}(const std::string& name, $environmentOrContainer)"""
else with(PrependOperator) {
"""
|${reactor.name}(
| const std::string& name,
| $environmentOrContainer,
${" | "..parameterArgs.joinToString(",\n", postfix = ")")}
""".trimMargin()
}
}

private fun innerSignature(): String {
val args = parameterArguments(false)
return when (args.size) {
0 -> "Inner(reactor::Reactor* reactor)"
1 -> "Inner(reactor::Reactor* reactor, ${args[0]})"
else -> with(PrependOperator) {
"""
|Inner(
| reactor::Reactor* reactor,
${" | "..args.joinToString(",\n")})
""".trimMargin()
}
return """
${reactor.name}(
const std::string& name,
$environmentOrContainer,
${parameterArgs.joinToString(",\n", postfix = ")")}
""".trimIndent()
} else {
return "${reactor.name}(const std::string& name, $environmentOrContainer)"
}
}

/** Get the constructor declaration */
fun generateDeclaration() = "${signature(true)};"
/** Get the constructor declaration of the outer reactor class */
fun generateOuterDeclaration() = "${outerSignature(true)};"

/** Get the constructor definition */
fun generateDefinition(): String {
/** Get the constructor definition of the outer reactor class */
fun generateOuterDefinition(): String {
val innerParameters = listOf("this") + reactor.parameters.map { it.name }
return with(PrependOperator) {
"""
|${reactor.templateLine}
|${reactor.templateName}::${signature(false)}
|${reactor.templateName}::${outerSignature(false)}
| : reactor::Reactor(name, ${if (reactor.isMain) "environment" else "container"})
${" | "..parameters.generateInitializers()}
${" | "..state.generateInitializers()}
${" | "..instances.generateInitializers()}
${" | "..timers.generateInitializers()}
${" | "..actions.generateInitializers()}
${" | "..reactions.generateReactionViewInitializers()}
| , __lf_inner(${innerParameters.joinToString(", ") })
|{}
""".trimMargin()
}
}

/** Get the constructor declaration of the inner reactor class */
fun generateInnerDeclaration() = "${innerSignature()};"

fun generateInnerDefinition(): String {
return with(PrependOperator) {
"""
|${reactor.templateLine}
|${reactor.templateName}::Inner::${innerSignature()}
| : LFScope(reactor)
${" | "..parameters.generateInitializers()}
${" | "..state.generateInitializers()}
|{}
""".trimMargin()
}
Expand Down
7 changes: 5 additions & 2 deletions org.lflang/src/org/lflang/generator/cpp/CppExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,14 @@ fun Time.toCode() = TimeValue(this.interval.toLong(), this.unit).toCode()
/** Convert a value to a time representation in C++ code*
*
* If the value evaluates to 0, it is interpreted as a time.
* FIXME this is redundant to GeneratorBase.getTargetTime
*
* @param outerContext A flag indicating whether to generate code for the scope of the outer reactor class.
* This should be set to false if called from code generators for the inner class.
*/
fun Value.toTime(): String = when {
fun Value.toTime(outerContext: Boolean = false): String = when {
this.time != null -> this.time.toCode()
this.isZero -> TimeValue(0, TimeUnit.NONE).toCode()
outerContext && this.parameter != null -> "__lf_inner.${parameter.name}"
else -> this.toText()
}

Expand Down
20 changes: 10 additions & 10 deletions org.lflang/src/org/lflang/generator/cpp/CppInstanceGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,19 @@ class CppInstanceGenerator(
private val errorReporter: ErrorReporter,
) {

private val Instantiation.type: String
val Instantiation.cppType: String
get() {
return if (this.reactor.isGeneric)
"""${this.reactor.name}<${this.typeParms.joinToString(", ") { it.toText() }}>"""
return if (reactor.isGeneric)
"""${reactor.name}<${typeParms.joinToString(", ") { it.toText() }}>"""
else
this.reactor.name
reactor.name
}

private fun generateDeclaration(inst: Instantiation): String {
return if (inst.isBank)
"std::array<std::unique_ptr<${inst.type}>, ${inst.getValidWidth()}> ${inst.name};"
"std::array<std::unique_ptr<${inst.cppType}>, ${inst.getValidWidth()}> ${inst.name};"
else
"std::unique_ptr<${inst.type}> ${inst.name};"
"std::unique_ptr<${inst.cppType}> ${inst.name};"
}

private fun Instantiation.getParameterValue(param: Parameter, instanceId: Int? = null): String {
Expand All @@ -74,21 +74,21 @@ class CppInstanceGenerator(
return if (inst.isBank) {
val initializations = if (parameters.isEmpty()) {
(0 until inst.getValidWidth()).joinToString(", ") {
"""std::make_unique<${inst.type}>("${inst.name}_$it", this)"""
"""std::make_unique<${inst.cppType}>("${inst.name}_$it", this)"""
}
} else {
(0 until inst.getValidWidth()).joinToString(", ") {
val params = parameters.joinToString(", ") { param -> inst.getParameterValue(param, it) }
"""std::make_unique<${inst.type}>("${inst.name}", this, $params)"""
"""std::make_unique<${inst.cppType}>("${inst.name}", this, $params)"""
}
}
""", ${inst.name}{{$initializations}}"""
} else {
if (parameters.isEmpty())
""", ${inst.name}(std::make_unique<${inst.type}>("${inst.name}", this))"""
""", ${inst.name}(std::make_unique<${inst.cppType}>("${inst.name}", this))"""
else {
val params = parameters.joinToString(", ") { inst.getParameterValue(it) }
""", ${inst.name}(std::make_unique<${inst.type}>("${inst.name}", this, $params))"""
""", ${inst.name}(std::make_unique<${inst.cppType}>("${inst.name}", this, $params))"""
}
}
}
Expand Down
33 changes: 21 additions & 12 deletions org.lflang/src/org/lflang/generator/cpp/CppPortGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,31 @@ import org.lflang.lf.Reactor

class CppPortGenerator(private val reactor: Reactor, private val errorReporter: ErrorReporter) {

private fun generateDeclaration(port: Port): String {
val portType = when (port) {
is Input -> "reactor::Input"
is Output -> "reactor::Output"
else -> throw AssertionError()
}

return if (port.isMultiport) {
val width = port.getValidWidth()
val initializerLists = (0 until width).joinToString(", ") { """{"${port.name}_$it", this}""" }
"""std::array<$portType<${port.targetType}>, ${port.getValidWidth()}> ${port.name}{{$initializerLists}};"""
private fun generateDeclaration(port: Port): String = with(port) {
return if (isMultiport) {
val initializerLists = (0 until getValidWidth()).joinToString(", ") { """{"${name}_$it", this}""" }
"""$cppType $name{{$initializerLists}};"""
} else {
"""$portType<${port.targetType}> ${port.name}{"${port.name}", this};"""
"""$cppType $name{"$name", this};"""
}
}

/** Get the C++ type for the receiving port. */
val Port.cppType: String
get() {
val portType = when (this) {
is Input -> "reactor::Input"
is Output -> "reactor::Output"
else -> throw AssertionError()
}

return if (isMultiport) {
"std::array<$portType<$targetType>, ${getValidWidth()}>"
} else {
"$portType<$targetType>"
}
}

/**
* Calculate the width of a multiport.
*
Expand Down
Loading

0 comments on commit bd6ad65

Please sign in to comment.