c#跨平台移动开发_解释了使用C ++进行跨平台移动和Web开发

c#跨平台移动开发

The code for this part is available here https://github.com/skonstant/xptuto/tree/part3_gtest

这部分的代码在这里https://github.com/skonstant/xptuto/tree/part3_gtest

Google测试框架 (Google Test Framework)

It seems unit testing has become a religion these days. People often forget that writing tests is writing code: it increases the bug surface and the development time. I am not a unit test extremist but in our case, they come in handy.

如今,单元测试似乎已成为一种宗教。 人们常常忘记编写测试就是编写代码:它增加了错误的面以及开发时间。 我不是单元测试的极端主义者,但就我们而言,它们派上了用场。

Because I write graphical software, one feature could be a few screens and a dozen clicks away. So testing it in runtime can quickly become tedious. It is a little easier on Android where you have instant run or whatever they call it these days, and also perhaps on iOS if you enable app state preservation. I do it of course, but anyway, it is quicker to press a button in the IDE to try out a little piece of code.

因为我写的是图形软件,所以其中一个功能可能是几个屏幕和十二次单击。 因此,在运行时对其进行测试会很快变得乏味。 这是在Android上,你必须时刻运行或不管他们叫这些天变得更轻松,并且还可能在iOS上,如果您启用应用程序状态保存 。 我当然会这样做,但是无论如何,按下IDE中的按钮来尝试一些代码会更快。

We are lucky, the Google Test framework is there for us and is supported by our IDEs: QtCreator, pictured below, and CLion (and also Appcode).

幸运的是,Google Test框架已为我们所用,并得到我们的IDE的支持:QtCreator(如下图所示)和CLion(以及Appcode)。

We have now added a folder to our codebase, we call it “test”, and we will put our tests here. Google Test is well documented. Easy to use and non intrusive. You do not need it in your repo, CMake downloads and compiles it for us. We have added a CMakeLists.txt.in file in the test folder and now we have gtest.

现在,我们在代码库中添加了一个文件夹,我们将其称为“测试”,然后将测试放在这里。 Google Test有据可查 。 易于使用且无干扰。 您不需要在仓库中使用它,CMake会为我们下载并编译它。 我们在测试文件夹中添加了一个CMakeLists.txt.in文件,现在我们有了gtest。

We defined a test suite that will test our code:

我们定义了一个测试套件来测试我们的代码:

class Xptuto : public ::testing::Test {protected:
void SetUp() override {
stubHttp = std::make_shared<HttpClientStub>();
promise = std::make_shared<std::promise<void>>();
future = promise->get_future();
}
std::shared_ptr<HttpClientStub> stubHttp;
std::shared_ptr<std::promise<void>> promise;
std::future<void> future;
};TEST_F(Xptuto, GetUsersEmptyTest) {
auto instance = ::xptuto::Xptuto::make_instance(stubHttp);
stubHttp->path = "/responses/users_aosp.json";
auto p = promise;
instance->get_users(std::make_shared<GetUsersCbImpl>(
[p](const std::vector<xptuto::User> &users) {
EXPECT_FALSE(users.empty());
p->set_value();
}, [p](const std::string &) {
FAIL();
}
));
if (future.wait_for(1s) != std::future_status::ready) {
FAIL();
}
}
...

Our code has grown quite a bit by now :-) so what do we test here? First, we added a HttpClient parameter to our main class, this will do the Http GET calls only. We have not implemented it (it is in the next story). We have stubbed it.

到目前为止,我们的代码已经增长了很多:-)那么我们在这里进行什么测试? 首先,我们在主类中添加了HttpClient参数,这只会执行Http GET调用。 我们尚未实现它(在下一个故事中)。 我们已经存根了。

void HttpClientStub::get(const std::string &url, const std::shared_ptr<HttpCallback> &callback) {
auto p = path.value();
std::thread t([p](const std::shared_ptr<xptuto::HttpCallback> &cb) {
auto stream = std::ifstream(std::string(TEST_PATH) + p);
auto response = std::string((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>());
cb->on_response({response, 200});
}, callback);
t.detach();
}

This loads a text file and returns it in the response with HTTP code 200. In another thread because our Http client will be asynchronous all through.

这将加载一个文本文件,并使用HTTP代码200在响应中返回它。在另一个线程中,因为我们的Http客户端将一直是异步的。

So what do we test here? We test the get_users() method, it takes a callback. It makes an HTTP call, parses the reply and gives us a list of users.

那么我们在这里测试什么? 我们测试get_users()方法,它需要一个回调。 它进行HTTP调用,解析答复并提供给我们用户列表。

We use promise / future to handle the ansynchronous-ness of the code.

我们使用promise / future来处理代码的异步性。

We can see future waits “1s” guess what it means? This is part of std::chrono_literals.

我们可以看到未来的等待次数为“ 1”,这意味着什么? 这是std :: chrono_literals的一部分。

At this point we have stayed in the C++, implemented the get_users() code, the parsing of the response and we have stubbed the Http client. All these interfaces are of course defined in our Djinni code:

至此,我们停留在C ++中,实现了get_users()代码,解析了响应,并且对Http客户端进行了存根处理。 当然,所有这些接口都是在我们的Djinni代码中定义的:

get_users_cb = interface +j +c {
on_success(users : list<user>);
on_error(error: string);
}http_client = interface +j +c {
get(url: string, callback: http_callback);
}...

Notice here something new, “+j” this means this can be implemented in Java, yes, the users callback will be used in the view layer. In the meantime we have implemented it in C++ so we can use it in tests, and we will use it in the iOS view too. Here it is:

在这里注意一些新的“ + j ”,这意味着它可以用Java实现,是的,用户回调将在视图层中使用。 同时,我们已经用C ++实现了它,因此我们可以在测试中使用它,我们也将在iOS视图中使用它。 这里是:

class GetUsersCbImpl : public xptuto::GetUsersCb {public:
GetUsersCbImpl(
std::function<void(const std::vector<xptuto::User> &)> successFn,
std::function<void(const std::string &)> failureFn);
void on_success(const std::vector<xptuto::User> &users) override;
...private:
const std::function<void(const std::vector<xptuto::User> &)> successFn;
...
};

Notice the functional stuff, this allows to use this close to the way we would use it in Java with anonymous concrete classes, thanks to the C++11 lambdas. Quick example:

注意这些功能性的东西,这要感谢C ++ 11 lambdas的使用,使它可以接近我们在Java中使用匿名具体类的方式。 快速示例:

instance->get_users(std::make_shared<GetUsersCbImpl>(
[p](const std::vector<xptuto::User> &users) {
EXPECT_FALSE(users.empty());
p->set_value();
}, [p](const std::string &) {
FAIL();
}
));

It does not get more concise. Yes, I leave the std:: on purpose.

它不会变得更加简洁。 是的,我故意离开了std ::。

For parsing the JSON, I use the fantastic library by N. Lohmann, many thanks to him and the contributors:

对于解析JSON,我使用N. Lohmann的出色库,非常感谢他和贡献者:

It is a configure only library, if I can say, here is how you “parse” a user:

如果可以说,这是一个仅配置的库,这是您“解析”用户的方式:

namespace nlohmann {
template<>
struct adl_serializer<User> {
static User from_json(const json &j) {
return {
j["login"].get<std::string>(),
j["id"].get<int32_t>(),
j["avatar_url"].get<std::string>(),
...
}
...
};
};// and you get the user:User user = nlohmann::json::parse(json_string);

It’s all magic. And checked at compile time, if it does not know how to parse a user, it won’t compile, notice the lack of cast. It handles collections by itself too. There is support for JSON Schema validation too. I use that in my projects. Maybe in another article.

都是魔术。 并在编译时进行检查,如果它不知道如何解析用户,则不会编译,请注意缺少强制转换。 它也自己处理集合。 也支持JSON模式验证。 我在项目中使用它。 也许在另一篇文章中。

We could of course write tests to see what happens if the JSON is incorrect, mimic network errors and all that…Notice that my serializer above will crash if “login” does not exist or is not a string, e.g. There are ways to check values, check the library documentation if you want to go deeper. I was used to Gson and this is by no means inferior, and it is portable.

我们当然可以编写测试,以查看如果JSON不正确,模拟网络错误以及所有类似情况会发生什么……请注意,如果“登录”不存在或不是字符串,则上述序列化程序将崩溃, 例如,有一些方法可以检查值,如果您想更进一步,请查阅库文档。 我已经习惯了Gson ,这绝不逊色,而且可移植。

运行测试 (Run the tests)

Use an IDE, you’ll go much faster, you can run the whole suite or one test at a time and you can debug them too.

使用IDE,您的运行速度将大大提高,您可以一次运行整个套件或一个测试,也可以对其进行调试。

Of course in this case, the tests run on your computer, you may want to run them on your target devices, I never went that route, it is complex to change the main() on iOS or Android to run what you want (it can be done), I do not see the added value. main() is not ours here, it comes from gtest, even worse.

当然,在这种情况下,测试在您的计算机上运行,​​您可能想在目标设备上运行它们,我从没走过那条路,更改iOS或Android上的main()以运行您想要的(它很复杂)很复杂。可以做到),我看不到增加的价值 main()在这里不是我们的,它来自gtest,甚至更糟。

However, on web… you can run main() directly 😁

但是 ,在网络上……您可以直接运行main() 😁

Image for post
Gtest test suite running in Chrome
在Chrome中运行的Gtest测试套件

How cool is that? And it’s easy (in the build folder of your tests):

多么酷啊? 而且很容易(在测试的build文件夹中):

emrun — browser chrome xptutotest.html

emrun —浏览器chrome xptutotest.html

You have to load the emsdk environment first. In your emsdk folder:

您必须先加载emsdk环境。 在您的emsdk文件夹中:

source emsdk_env.sh

源emsdk_env.sh

I load it in Chrome, you can try in Firefox too, my tests here do not run in node because they use threads, which are not supported by node yet. (Yes we have threads in the browser now, and not that worker ultra basic crap, real POSIX threads). Threads will be the topic of another article, it is complex, because we are in GUI software here.

我将其加载到Chrome中,也可以在Firefox中尝试,此处的测试未在节点上运行,因为它们使用线程,而节点尚不支持线程。 (是的,我们现在在浏览器中有线程,而不是该工作程序超基本的废话,真正的POSIX线程)。 线程将是另一篇文章的主题,这很复杂,因为我们在这里使用GUI软件。

One last goodie…

最后一个礼物……

使用ccache (Use ccache)

ccache is a cache for C compilers, it is some serious tool developped by SAMBA used by most large projects like the Linux Kernel and Android Open Source Project. It will store compiled objects so you don't have to compile them again if they are unchanged. This works much better than the XCode build folder that you have to continually delete because things stop working all of a sudden.

ccache是C编译器的缓存,它是SAMBA开发的一种严肃的工具,被大多数大型项目(例如Linux内核和Android开放源代码项目)使用。 它将存储已编译的对象,因此如果它们不变,则不必再次编译它们。 这比您必须不断删除的XCode build文件夹要好得多,因为事情突然停止了。

We add it in our CMakeLists.txt so it is used when we compile for the local machine, web and Android:

我们将其添加到CMakeLists.txt中,以便在为本地计算机,Web和Android进行编译时使用:

find_program(CCACHE_FOUND ccache)if (CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE_FOUND})
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE_FOUND})endif (CCACHE_FOUND)

For Xcode, we need a little script that will choose ccache if it is available or switch to clang.

对于Xcode,我们需要一个小的脚本,如果可用,它将选择ccache或切换到clang

#!/bin/sh
if
[ -x "$(command -v ccache)" ]; then
exec ccache clang++ "$@"
else
exec clang++ "$@"
fi

We then need to tell Xcode to use these commands as the C (and Objective-C) and C++ (and Objective-C++) compilers:

然后,我们需要告诉Xcode将这些命令用作C(和Objective-C)和C ++(和Objective-C ++)编译器:

Image for post
At the bottom of the Build settings.
在构建设置的底部。

And we need to tell Xcode not to use the clang modules, a non standard feature that is not supported by ccache.

而且我们需要告诉Xcode不要使用clang 模块 ,这是ccache不支持的非标准功能。

Image for post

You may not see a difference in the beginning but as your project grows to hundreds of files, you will feel a major difference: compilation is instant, only linking remains to be done.

您一开始可能没有什么不同,但是随着您的项目增长到成百上千个文件,您会感觉到一个主要的区别:编译是即时的,只有链接需要完成。

ccache is available in Brew for Mac OS. It is par of all Linux distributions, it works on Windows too. And… it works with Objective-C, save time, stop watching your computer compile.

在Mac OS的Brew中,可以使用ccache。 它是所有Linux发行版的标准,也可以在Windows上运行。 而且…它可与Objective-C一起使用,节省时间,不再需要监视计算机的编译。

This is it for Unit testing JSON and basic threading, next we will make real ReST calls.

这是用于JSON和基本线程测试的单元,接下来我们将进行真正的ReST调用。

另请阅读: (Also read:)

介绍: (Introduction:)

We present our technical choice.

我们介绍我们的技术选择。

1. 项目设置 (1. Project Setup)

We will configure a project that compiles and runs on iOS/Xcode, Android Studio and Emscripten/CMake and show how to run and debug the 3 of them.

我们将配置一个在iOS / Xcode,Android Studio和Emscripten / CMake上编译并运行的项目,并说明如何运行和调试这三个项目。

2.传递物体 (2. Pass objects around)

In this one we will show how to pass objects around from the business logic layer to the various view layers.

在这一部分中,我们将展示如何将对象从业务逻辑层传递到各个视图层。

4. ReST客户端 (4. ReST Client)

We implement a minimal ReST client using the platform HTTP implementations.

我们使用平台HTTP实现来实现最小的ReST客户端。

5.多线程 (5. Multi-Threading)

Yes, you can use threads in all three environments in a portable way!

是的,您可以以可移植的方式在所有三个环境中使用线程!

6.使用SQLite脱机数据 (6. Offline data with SQLite)

Adding SQLite database is simple as ever.

添加SQLite数据库非常简单。

翻译自: https://medium.com/swlh/cross-platform-mobile-and-web-development-with-c-explained-8fb5fb916d35

c#跨平台移动开发

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值