Flutter platform channels pass "multi-data" to windows function - windows

I am testing the Platform-Channels with the windows visual studio 2022.
I can pass the single value from the flutter UI while I get the battery value, but I want to send multi-data from flutter to the windows code.
Here is mine test code which I follow the flutter tutorial:
class _MyHomePageState extends State<MyHomePage> {
static const MethodChannel platform =
MethodChannel('samples.flutter.dev/battery');
String _batteryLevel = 'Unknown battery level.';
int value = 100;
Future<void> _getBatteryLevel(int fluval) async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel', fluval);
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
#override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
_getBatteryLevel(100);
},
child: const Text('Get Battery Level'),
),
Text(_batteryLevel),
],
),
),
);
}
}
and the windows side:
void initMethodChannel(flutter::FlutterEngine* flutter_instance) {
const static std::string channel_name("samples.flutter.dev/battery");
auto channel =
std::make_unique<flutter::MethodChannel<>>(
flutter_instance->messenger(), channel_name,
&flutter::StandardMethodCodec::GetInstance());
channel->SetMethodCallHandler(
[](const flutter::MethodCall<>& call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {
// cheack method name called from dart
if (call.method_name().compare("getBatteryLevel") == 0) {
int battery_level = GetBatteryLevel();
const auto* flu_value = std::get_if<int>(call.arguments());
std::cout << typeid(flu_value).name() << std::endl;
if (!flu_value) {
result->Error("Missing required type parameter","Expected int");
}
std::cout << "From Flutter: " << *flu_value << std::endl;
if (battery_level != -1) {
result->Success(battery_level);
}
else {
result->Error("UNAVAILABLE", "Battery level not available.");
}
}
else {
result->NotImplemented();
}
});
}
if I want to send multi-data like this:
Future<void> _getBatteryLevel(int fluval, int data2,...,...) ...
How should I do or parse these two values fluval and data2 when call once invokeMethod?
Any examples that I can reference? or tips for the Windows UI design?
I try to search but the most answer is for the Android or IOS app, and the android answer is using call.argument("value") which you can choose specific data value to use but the windows only has call.arguments(); function call, I don't know how to get the specific data I want.
============================================================
[Update]
From the #Richard tips and example,
I made a test and it works, thanks #Richard!
Here is the Flutter UI example code: (inside the Stateful Widget State class), fluval= 100, fluval2 = 87
String _getBackV1 = 'Unknown Value 1.';
String _getBackV2 = 'Unknown Value 2.';
int value = 100;
Future<void> _testMapPassValue(int fluval, int fluval2) async {
String backValue1, backValue2;
try {
final Map<String, dynamic>? getReply = await platform
.invokeMapMethod<String, dynamic>('testPass',
<String, dynamic>{"fluval": fluval, "fluval2": fluval2});
backValue1 = "Get Back Value 1 from windows: ${getReply!["fluval"]}";
backValue2 = "Get Back Value 2 from windows: ${getReply!["fluval2"]}";
} on PlatformException catch (e) {
backValue1 = "Failed to get Value 1: '${e.message}'.";
backValue2 = "Failed to get Value 2: '${e.message}'.";
}
setState(() {
_getBackV1 = backValue1;
_getBackV2 = backValue2;
});
}
At the Windows side example code:
using flutter::EncodableList;
using flutter::EncodableMap;
using flutter::EncodableValue;
constexpr char testMapPassValue[] = "testPass";
constexpr char passKey1[] = "fluval";
constexpr char passKey2[] = "fluval2";
The method call handler:
if (call.method_name().compare(testMapPassValue) == 0) {
const auto* getArguments_flu = std::get_if<flutter::EncodableMap>(call.arguments());
assert(getArguments_flu);
return Flu_2_Win_MethodHandler(*getArguments_flu, std::move(result));
}
void Flu_2_Win_MethodHandler(
const EncodableMap& args, std::unique_ptr<flutter::MethodResult<>> result) {
int wins2fluValue1;
int wins2fluValue2;
EncodableMap win2fluMap;
const auto* flu2winsValue1 =
std::get_if<int>(ValueOrNull(args, passKey1));
if (!flu2winsValue1) {
return result->Error("argument_error",
std::string(passKey1) + " argument missing");
}
wins2fluValue1 = *flu2winsValue1;
std::cout << "From Flutter value1: " << *flu2winsValue1 << std::endl;
wins2fluValue1 = wins2fluValue1 + 10;
std::cout << "After calculate value1: " << wins2fluValue1 << std::endl;
const auto* flu2winsValue2 =
std::get_if<int>(ValueOrNull(args, passKey2));
if (!flu2winsValue2) {
return result->Error("argument_error",
std::string(passKey2) + " argument missing");
}
wins2fluValue2 = *flu2winsValue2;
std::cout << "From Flutter value1: " << *flu2winsValue2 << std::endl;
wins2fluValue2 = wins2fluValue2 + 20;
std::cout << "After calculate value2: " << wins2fluValue2 << std::endl;
win2fluMap.insert(std::pair<std::string, int>("fluval", wins2fluValue1));
win2fluMap.insert(std::pair<std::string, int>("fluval2", wins2fluValue2));
return result->Success(std::move(EncodableValue(win2fluMap)));
}
and the final result:
Thank you!

You can only pass a single entity as argument or result, but that entity can be any of the supported types (whether primitive - int, double, etc - or complex - list or map).
The mapping between Dart and C++ (native for Windows) types is:
// std::monostate -> null
// bool -> bool
// int32_t -> int
// int64_t -> int
// double -> double
// std::string -> String
// std::vector<uint8_t> -> Uint8List
// std::vector<int32_t> -> Int32List
// std::vector<int64_t> -> Int64List
// std::vector<float> -> Float32List
// std::vector<double> -> Float64List
// EncodableList -> List
// EncodableMap -> Map
(see encodable_value.h)
So, to send your two integers (fluval and data2) you could choose to put them in a Dart list (i.e. <int>[123, 456]) or a Dart map (i.e. {'fluval':123, 'data2':456}). At the native end, arguments will be either an EncodableList or EncodableMap.
Here's an example of how a complex Dart structure would appear at the native end:
// {
// 'flag': true,
// 'name': 'Thing',
// 'values': [1, 2.0, 4],
// }
// would correspond to:
// EncodableValue(EncodableMap{
// {EncodableValue("flag"), EncodableValue(true)},
// {EncodableValue("name"), EncodableValue("Thing")},
// {EncodableValue("values"), EncodableValue(EncodableList{
// EncodableValue(1),
// EncodableValue(2.0),
// EncodableValue(4),
// })},
// })
The camera plugin has some example usage (to get the arguments as a map):
const auto* arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());
In the same file, check out the two utility functions:
// Looks for |key| in |map|, returning the associated value if it is present, or
// a nullptr if not.
const EncodableValue* ValueOrNull(const EncodableMap& map, const char* key) {
auto it = map.find(EncodableValue(key));
if (it == map.end()) {
return nullptr;
}
return &(it->second);
}
// Looks for |key| in |map|, returning the associated int64 value if it is
// present, or std::nullopt if not.
std::optional<int64_t> GetInt64ValueOrNull(const EncodableMap& map,
const char* key) {
auto value = ValueOrNull(map, key);
if (!value) {
return std::nullopt;
}
if (std::holds_alternative<int32_t>(*value)) {
return static_cast<int64_t>(std::get<int32_t>(*value));
}
auto val64 = std::get_if<int64_t>(value);
if (!val64) {
return std::nullopt;
}
return *val64;
}
and their typical usage:
const auto* camera_name =
std::get_if<std::string>(ValueOrNull(args, kCameraNameKey));
auto camera_id = GetInt64ValueOrNull(args, kCameraIdKey);

Related

c++ Inset and read Map element in protobuf3

Here is the .proto which have one map:
map.proto
syntax = "proto3";
package demo;
message Person {
map<string, int32> family_list = 4;
}
Now I insert key and value from the Write_impl.cc
auto map = test.mutable_family_list();
string key = "faimly";
int val =20;
(*map)[key] = val;
std::cout<<"map = "<<(*map)[key]<<std::endl;
Below I read the value of key family in read_impl.cc
auto test = demo::Person::default_instance();
auto map = test.mutable_family_list();
std::cout<<"map = "<<(*map)["faimly"]<<std::endl;
Problem: I get 0 when reading the value of key "family"
You are using demo::Person::default_instance() which doesn't have the value you've stored earlier. It contains the default one.
You are using the subscript operator [] that doesn't throw an exception if the key is not found but the at() method does. You should use the at() method.
Here's an example of serialization and deserialization:
int main()
{
// Serialization
demo::Person sPerson;
const auto mutable_family_list = sPerson.mutable_family_list();
mutable_family_list->insert( { "abc", 42 } );
std::cout << mutable_family_list->at( "abc" ) << '\n';
const auto serialized = sPerson.SerializeAsString();
// Deserialization
demo::Person dPerson;
if ( !dPerson.ParseFromString( serialized ) )
{
std::cerr << "Deserialization failed!\n";
return -1;
}
const auto family_list = dPerson.family_list();
std::cout << family_list.at("abc") << '\n';
return 0;
}
Output:
42
42
Alternatively, you can find() first and then use the value like this:
const auto it = family_list.find( "abc" );
if ( it != family_list.end() )
{
std::cout << it->second << '\n';
}

What might cause a value that is only set at initialization to get corrupted?

I have the following code
typedef struct{
uint8_t carOpening:2;
uint8_t fCarElement:4;
}lockState_t;
enum Doors
{
DRIVER_DOOR = 0,
PASSENGER_DOOR,
...
NUMBER_OF_ELEMENTS
};
class DoorState
{
public:
DoorState(Doors door, uint8_t initialState)
{
fState.fCarElement = static_cast<uint8_t>(door);
if(initialState == SIG_VAL_DOOR_CLOSED)
{
fState.carOpening = static_cast<uint8_t>(DOOR_CLOSED);
}
else if(initialState == SIG_VAL_DOOR_OPENED)
{
fState.carOpening = static_cast<uint8_t>(DOOR_OPENED);
}
else
{
fState.carOpening = static_cast<uint8_t>(DOOR_UNKNOWN);
}
};
void checkSignal(uint8_t signal);
enum tState { DOOR_UNKNOWN = 0, DOOR_CLOSED, DOOR_OPENED };
private:
static const uint8_t SIG_VAL_DOOR_CLOSED = 0x00;
static const uint8_t SIG_VAL_DOOR_OPENED = 0x01;
lockState_t fState;
};
Then I have a few instances of this class, like:
DoorState fDriverDoorState;
DoorState fPassengerDoorState;
This is how I initialize the instances:
, fDriverDoorState(DRIVER_DOOR
, DS::instance().get<uint8_t>(DS::ComStatZvKlappen_StDswDrd_ID))
, fPassengerDoorState(PASSENGER_DOOR, DS::instance().get<uint8_t>(DS::ComStatZvKlappen_StDswPsd_ID))
Then I am checking if the state of the door has changed and publising a message if it has:
tState doorState = DOOR_UNKNOWN;
if (SIG_VAL_DOOR_CLOSED == signal)
{
doorState = DOOR_CLOSED;
}
else if (SIG_VAL_DOOR_OPENED == signal)
{
doorState = DOOR_OPENED;
}
if(doorState != static_cast<tState>(fState.carOpening))
{
fState.carOpening = static_cast<uint8_t>(doorState);
if(DOOR_UNKNOWN != doorState)
{
LooperEventBroker::publish(::csm::events::CsmEvents::VEHICLE_LOCK_STATE_EVENT(), &fState);
Logger::info(VEHICLE, "Door %s event published for %s",
doorState == DOOR_OPENED ? "openend" : "closed", opening2string(fState.fCarElement));
}
}
For the most part it is working perfectly. The strange part is that sometimes I see this log being posted:
"Door closed event published for INVALID DOOR", which should not happen as no instance is initialized with INVALID DOOR, and fCarElement, which stores the door type, is never modified. Which means that somehow fCarElement gets corrupted. Question is where or why?
As requested here is the implementation of opening2string function:
static const char* opening2string(uint8_t opening)
{
switch (opening)
{
case DRIVER_DOOR:
return "DRIVER DOOR";
case PASSENGER_DOOR:
return "PASSENGER DOOR";
...
default:
return "INVALID DOOR";
}
}
Regards,
Gabriel

std::map<my_enum, base_class> insert error: base_class is an inaccessibile base of derived_class

This little program has to write an xml file.
Building the code I get the following error:
K:\Sergio\cpp\xml\sergio\cbp6s\main.cpp|32|error: 'base_tag' is an inaccessible base of 'tag'
In short, I have two classes derived from base_tag (xml_declaration and tag) and I want insert (or emplace) in a std::map some std::pair elements.
Building, the first insert works (std::pair<order::declaration, xml_declaration>), but the second fails (std::pair<order::root, tag_object>).
Between the object are derived from base_tag
Where am I wrong?
File tag.hpp :
#ifndef _TAG_HPP_
#define _TAG_HPP_
#include <string>
#include <vector>
#include <utility>
enum class tag_type
{
closing = -1,
autoclosed = 0,
opening = 1
};
using attribute = std::pair<std::string, std::string>;
class base_tag
{
public:
base_tag();
virtual ~base_tag();
virtual std::string get();
bool attribute_exists(std::string);
std::string get_attribute_value(std::string name);
bool add_attribute(std::string name, std::string value);
protected:
std::vector<std::pair<std::string, std::string>> _attributes;
};
// ------------------------------------------------------------
class xml_declaration : public base_tag
{
public:
xml_declaration();
~xml_declaration();
std::string get();
};
// ----------------------------------------------------------
class tag : base_tag
{
public:
tag(std::string name);
tag(std::string name, tag_type tt );
std::string get();
void set_ident(size_t ident);
protected:
std::string _name;
tag_type _tt;
};
#endif // _TAG_HPP_
File tag.cpp
#include "tag.hpp"
base_tag::base_tag()
{
}
base_tag::~base_tag()
{
_attributes.clear();
}
bool base_tag::attribute_exists(std::string name)
{
bool res = false;
for(auto & i : _attributes)
{
if (i.first == name)
res = true;
}
return res;
}
bool base_tag::add_attribute(std::string name, std::string value)
{
bool res = false;
if(!attribute_exists(name))
{
attribute a = std::make_pair(name, value);
_attributes.push_back(a);
res = true;
}
return res;
}
std::string base_tag::get()
{
return u8"<invalid_tag/>";
}
// -------------------------------------------------------
xml_declaration::xml_declaration(): base_tag()
{
add_attribute("version", "1.0");
add_attribute("encoding", "UTF-8");
add_attribute("standalone", "yes");
}
xml_declaration::~xml_declaration()
{ }
std::string xml_declaration::get()
{
std::string res = u8"<?xml";
for(auto & i : _attributes)
{
res.push_back(' ');
res += i.first;
res += u8"=\"";
res += i.second;
res += u8"\"";
}
res += u8" ?>";
return res;
}
// -------------------------------------------------------------
tag::tag(std::string name):base_tag(), _name(name)
{
_tt = tag_type::autoclosed;
}
tag::tag(std::string name, tag_type tt ): base_tag(), _name(name),
_tt(tt)
{ }
std::string tag::get()
{
std::string res = u8"";
bool with_attributes = !(_attributes.empty());
switch(_tt)
{
case tag_type::autoclosed : {
res = u8"<";
res += _name;
if(with_attributes)
{
for(auto & i : _attributes)
{
res.push_back(' ');
res += i.first;
res += u8"=\"";
res += i.second;
res += u8"\"";
}
};
res += u8"/>";
break;
}
case tag_type::opening : {
res = u8"<";
res += _name;
for(auto & i : _attributes)
{
res.push_back(' ');
res += i.first;
res += u8"=\"";
res += i.second;
res += u8"\"";
}
res += u8">";
break;
}
case tag_type::closing : {
res = u8"</";
res += _name;
res.push_back('>');
}
default : break;
}
return res;
}
File main.cpp
#include <iostream>
#include <map>
#include <utility>
#include "tag.hpp"
using namespace std;
enum class order
{
declaration = 0,
root = 1,
file_version = 4,
project = 10,
project_closing = 998,
root_closing = 1000
};
int main()
{
std::map<order, base_tag> tree;
xml_declaration decl;
cout << decl.get() << endl;
tag t1("project_file", tag_type::opening);
tag t2("project_file", tag_type::closing);
tree.insert(std::pair<order, base_tag>(order::declaration,
decl));
tree.insert(std::pair<order, base_tag>(order::root, t1));
tree.insert(std::pair<order, base_tag>(order::root, t2));
// tree.emplace(std::pair<order, base_tag>(order::root_closing, t1));
cout << tree.size() << endl;
return 0;
}
I'm using Code::Blocks with GCC 5.1.0 (on Windows 10).
As the error message says, tag inherits base_tag privately. If you change its headline into
class tag: public base_tag;
(same as your xml_declaration which inherits base_tag publicly) then it will officially be-a base tag.
A more serious problem is that you try to store instances of inherited classes in a container of a base class by value. What happens then is, objects get sliced and lose their whole derived functionality; you put an object of a derived class into a map, but you actually store an object of a based class in it (that's the reason some people tend to say polymorphic base classes should necessarily be abstract.) Use (smart) pointers or reference_wrappers as map's mapped type.

Type conversion, 'initializing': cannot convert from 'HtmlBuilder *' to 'HtmlElement'

I was trying to follow a tutorial and I am stuck badly at implementing the fluent builder for the unique_ptr as well.
Despite knowing that it is a type conversion thing and after inspecting the documentation I wasn't able to find a proper fix.
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <memory>
using namespace std;
class HtmlBuilder;
struct HtmlElement
{
string name;
string text;
vector<HtmlElement> elements;
const size_t indent_size = 2;
HtmlElement() {}
HtmlElement(const string& name, const string& text) : name{ name }, text{ text }
{
}
string str(int indent = 0) const // it is a const because it doesn't change the inner elements of htmlelement
{
ostringstream oss;
string i(indent_size * indent, ' '); // repeating a character as many times as required.
oss << i << "<" << name << ">" << endl;
if (text.size() > 0)
oss << string(indent_size * (indent + 1), ' ') << text << endl;
// recursive call
for (const auto& e : elements)
oss << e.str(indent + 1);
oss << i << "</" << name << ">" << endl;
return oss.str();
}
static HtmlBuilder build(string root_name);
static unique_ptr<HtmlBuilder> create(string root_name);
};
struct HtmlBuilder
{
HtmlBuilder(string root_name)
{
root.name = root_name;
}
HtmlElement root; // we can not do anything without root
HtmlBuilder& add_child(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
root.elements.emplace_back(e);
// it is a reference
return *this;
}
HtmlBuilder* add_child2(string child_name, string child_text)
{
HtmlElement e{ child_name, child_text };
// emplace_back will return a reference to element that was just created in the vector where as push_back does not return anything, so you could preform some chaining if you wanted
root.elements.emplace_back(e);
// it is a pointer
return this;
}
string str() const {
return root.str();
}
// let's you convert the builder to htmlelement.
// automatic conversion.
// it wil be converted only after the chaining has finished.
operator HtmlElement() { return root; }
/*B& operator= (const A& x) { return *this; }*/
//operator unique_ptr<HtmlElement>() {
// return root;
//}
};
// it had to be pasted here after definition of HtmlBuilder
HtmlBuilder HtmlElement::build(string root_name)
{
// it will get automatically converted to a html element due to
// automatic conversion.
return HtmlBuilder{ root_name };
}
unique_ptr<HtmlBuilder> HtmlElement::create(string root_name) {
return make_unique<HtmlBuilder>(root_name);
}
// Basically we want to create builder from HtmlElement itself
// and be able to add childs as many as we want and at the end
// it will still return an HtmlElement due to automatic conversion
// in this way we hide builder completely
int main()
{
HtmlBuilder builder{ "ul" };
builder.add_child("li", "hello")
.add_child("li", "world");
//HtmlElement e = builder;
// important: automatic conversion occurs only at the end, if the object itself is returned.
HtmlElement e = HtmlElement::build("ul").add_child("li", "test").add_child("li", "test2");
HtmlElement ee = HtmlElement::create("ul")->add_child2("li", "test")->add_child2("li", "test2");
cout << e.str() << endl;
getchar();
return 0;
}
The problem is at trying to use use this line:
HtmlElement::create("ul")->add_child2("li", "test")->add_child2("li", "test2");
It throws the error as explained above. It says that cannot convert from 'HtmlBuilder *' to 'HtmlElement'. Tried several solution but I am a beginner in C++ and have not managed to fix it so far.
You've got an operator() to convert from HtmlBuilder to HtmlElement, but not from HtmBuilder* to HtmlElement. That's the difference between your build() line and your create() line.
So you have to dereference the pointer returned by the create()->add_child2() line.
Try this
HtmlElement ee = *(HtmlElement::create("ul")->add_child2("li", "test")->add_child2("li", "test2"));
Running on Wandbox

Can I do this without a macro (in C++ 11)?

I have code like this:
void function()
{
auto isOk=task(1);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(2);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(3);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(4);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(5);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(6);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(7);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(8);
if(!isOk)
{
return;
}
// more code here
auto isOk=task(9);
if(!isOk)
{
return;
}
}
It should be noted that I can not put them in a loop (My code is similar to this but not exactly this code)
The if block is very ugly and I may be bale to write it as follow:
#define TASK(x) {if(!task(x)) return;}
void function()
{
TASK(1);
// more code here
TASK(2);
// more code here
TASK(3);
// more code here
TASK(4);
// more code here
TASK(5);
// more code here
TASK(6);
// more code here
TASK(7);
// more code here
TASK(8);
// more code here
TASK(9);
}
My question is:
Is there any better way to do this when I am using C++11?
The problem with this code is:
I can not debug it easily.
The macro is not inside a namespace and maybe conflict with other macros.
Update 1
As most of the answer here tries to solve the problem in the specific code, when I am looking for the general solution, I am asking specifc questions related to this code:
1- Can I use lambda to mimic the macro?
2- Can I use a constexpr to mimic a macro?
3- Any other way to mimic a MACRO in a compiler friendly way (with the same result as a macro) so I can easily debug them?
void function() {
if (!task(1)) return;
// code here
if (!task(2)) return;
// more code here
if (!task(3)) return;
// more code here
}
This is small and tight and no ugly bulky blocks.
If task(1) is much larger, you can put return; on the next line indented.
Instead of using a plain return, you could choose to use exceptions instead, which not only leave the current function, but all functions until they find a catch block.
Something like this:
void tryTask(int i){
auto isOk=task(i);
if(!isOk)
{
throw std::runtime_error("Task failed: Nr. "+to_string(i));
}
}
function()
{
tryTask(1);
// more code here
tryTask(2);
// more code here
tryTask(3);
...
}
This however lets your function throw an exception instead of just returning if one of the tasks failed. If this is not what you want, surround it either inside the function with a try-catch block or call it from a second function like this:
void callfunction(){
try{
function();
} catch (std::exception& e) {
//do whatever happens if the function failed, or nothing
}
}
If you have control about the task() function, you might also decide to throw the exception directly inside this function instead of returning a bool.
If you want to make sure you only catch your own exceptions, write a small class for this taking only the information you need for handling the exception (if you don't need any, an empty class will do the job) and throw/catch an instance of your class instead.
Here's a quick and dirty approach with lambdas.
Assuming this is your task function:
#include <iostream>
/** Returns 0 on success; any other returned value is a failure */
int task(int arg)
{
std::cout << "Called task " << arg << std::endl;
return arg < 3 ? 0 : 1;
}
Invoke the tasks in a chain as follows:
#include <iostream>
int main()
{
int result = Chain::start()
.and_then([]() -> int {return task(1);})
.and_then([]() -> int {return task(2);})
.and_then([]() -> int {return task(3);})
.and_then([]() -> int {return task(4);})
.and_then([]() -> int {return task(5);})
.and_then([]() -> int {return task(6);})
.and_then([]() -> int {return task(7);})
.and_then([]() -> int {return task(8);})
.and_then([]() -> int {return task(9);})
.result();
std::cout << "Chain result: " << result << std::endl;
return result;
}
Because the task returns success only when called with an argument value less than 3, the invocation chain stops as expected after the 3rd step:
$ ./monad
Called task 1
Called task 2
Called task 3
Chain result: 1
This is the implementation of the Chain class:
class Chain
{
public:
const int kSuccess = 0;
Chain() {_result = kSuccess;}
static Chain start() { return Chain(); }
Chain& and_then(std::function<int()> nextfn) {
if(_result == 0) {
_result = nextfn();
}
return *this;
}
int result() { return _result; }
private:
int _result;
};
I know, it looks ugly and it's non-generic. But if this is the general direction you were thinking of, let me know and we can evolve it.
I would put code to execute btw calling task into a vector and then run a loop:
const size_t steps = 9;
using ops = std::function<void()>;
std::vector<ops> vops(steps);
steps[0] = [] { /* some code here to execute after task 0 */ };
...
for( size_t i = 0; i < steps; ++i ) {
if( !task(i) ) return;
if( vops[i] ) (vops[i])();
}
You can use an integer sequence.
// No task to call without an integer.
bool function(std::index_sequence<>) { return true; }
template<std::size_t I, std::size_t... S>
bool function(std::index_sequence<I, S...>) {
return [](){
auto isOk = task(I)
if (!isOk) return false;
// some code
return true;
// it will call function with the rest of the sequence only if the lambda return true.
}() && function(std::index_sequence<S...>{});
}
void function() {
// this call with a integer sequence from 0 to 9
function(std::make_index_sequence<10>{});
}
This code will expand just as if you'd write it by hands.
If the code between calls of task is different for each step, you can use a tuple.
auto afterTask = std::make_tuple(
[](){ std::cout << "after task 0" << std::endl; },
[](){ std::cout << "after task 1" << std::endl; },
[](){ std::cout << "after task 2" << std::endl; },
[](){ std::cout << "after task 3" << std::endl; },
[](){ std::cout << "after task 4" << std::endl; },
[](){ std::cout << "after task 5" << std::endl; },
[](){ std::cout << "after task 6" << std::endl; },
[](){ std::cout << "after task 7" << std::endl; },
[](){ std::cout << "after task 8" << std::endl; },
[](){ std::cout << "after task 9" << std::endl; }
);
And then change the definition of function with:
template<std::size_t I, std::size_t... S>
bool function(std::index_sequence<I, S...>) {
return task(I) &&
(static_cast<void>(std::get<I>(afterTask)()), true) &&
function(std::index_sequence<S...>{});
}

Resources