📜 ⬆️ ⬇️

C ++ Enterprise Edition. Is it possible to?

C ++ Enterprise Edition


What is the "enterprise edition"


c ++ ee


Surprisingly, for all the time of my work in IT, I have never heard anyone say "enterprise edition" regarding a programming language, except for Java. But after all, applications for the corporate segment are written in many programming languages, and the entities with which programmers operate are, if not identical, they are similar. And for c ++ in particular, I would like to fill the gap of enterprisitism, at least by telling about it.


For C ++, the "enterprise edition" is a subset of language and libraries that allows you to "quickly" develop [cross-platform] applications for weakly coupled modular systems with distributed and / or cluster architecture and with applied business logic and, as a rule, high load.


To continue our conversation, first of all, you need to enter the concepts of application , module and plug-in.



All applications, while performing their unique work, usually need system-wide mechanisms, such as data access (DBMS), information exchange via a common bus (JMS), execution of distributed and local scripts while maintaining consistency (Transactions), processing requests coming for example, via the http (s) (fastcgi) protocol or via websockets, etc ... Each application must have the ability to orchestrate its modules (OSGI), and the distributed system must have the ability to orchestrate applications.


An example of a distributed weakly coupled system


system


application


An example of a "corporate" server application scheme.


application


I have already given a general definition of the application, so let's see what is now in the C ++ world for the implementation of this concept. The first showed the implementation of the application graphical frameworks such as Qt and GTK, but their application versions initially assumed that the application is a graphical “window” with its context and only after a while a general vision of the application appeared, including as a system service, for example qtservice . But we don’t really want to drag conditionally graphic framework for the service task, so let's look towards non-graphic libraries. And the first comes to mind is the boost ... But unfortunately in the list of official libraries there is no Boost. Application and the like There is a separate project Boost.Application . The project is very interesting, but, in my opinion, verbose, although the ideology boost is respected. Here is an example application from Boost. Application


#define BOOST_ALL_DYN_LINK #define BOOST_LIB_DIAGNOSTIC #define BOOST_APPLICATION_FEATURE_NS_SELECT_BOOST #include <fstream> #include <iostream> #include <boost/application.hpp> using namespace boost; // my application code class myapp { public: myapp(application::context& context) : context_(context) {} void worker() { // ... while (st->state() != application::status::stopped) { boost::this_thread::sleep(boost::posix_time::seconds(1)); if (st->state() == application::status::paused) my_log_file_ << count++ << ", paused..." << std::endl; else my_log_file_ << count++ << ", running..." << std::endl; } } // param int operator()() { // launch a work thread boost::thread thread(&myapp::worker, this); context_.find<application::wait_for_termination_request>()->wait(); return 0; } bool stop() { my_log_file_ << "Stoping my application..." << std::endl; my_log_file_.close(); return true; // return true to stop, false to ignore } private: std::ofstream my_log_file_; application::context& context_; }; int main(int argc, char* argv[]) { application::context app_context; // auto_handler will automatically add termination, pause and resume (windows) // handlers application::auto_handler<myapp> app(app_context); // to handle args app_context.insert<application::args>( boost::make_shared<application::args>(argc, argv)); // my server instantiation boost::system::error_code ec; int result = application::launch<application::server>(app, app_context, ec); if (ec) { std::cout << "[E] " << ec.message() << " <" << ec.value() << "> " << std::endl; } return result; } 

In the given example, the application myapp is defined with its main worker thread worker and the mechanism for launching this application.
As a supplement, I will give a similar example from the pocoproject framework


 #include <iostream> #include <sstream> #include "Poco/AutoPtr.h" #include "Poco/Util/AbstractConfiguration.h" #include "Poco/Util/Application.h" #include "Poco/Util/HelpFormatter.h" #include "Poco/Util/Option.h" #include "Poco/Util/OptionSet.h" using Poco::AutoPtr; using Poco::Util::AbstractConfiguration; using Poco::Util::Application; using Poco::Util::HelpFormatter; using Poco::Util::Option; using Poco::Util::OptionCallback; using Poco::Util::OptionSet; class SampleApp : public Application { public: SampleApp() : _helpRequested(false) {} protected: void initialize(Application &self) { loadConfiguration(); Application::initialize(self); } void uninitialize() { Application::uninitialize(); } void reinitialize(Application &self) { Application::reinitialize(self); } void defineOptions(OptionSet &options) { Application::defineOptions(options); options.addOption( Option("help", "h", "display help information on command line arguments") .required(false) .repeatable(false) .callback(OptionCallback<SampleApp>(this, &SampleApp::handleHelp))); } void handleHelp(const std::string &name, const std::string &value) { _helpRequested = true; displayHelp(); stopOptionsProcessing(); } void displayHelp() { HelpFormatter helpFormatter(options()); helpFormatter.setCommand(commandName()); helpFormatter.setUsage("OPTIONS"); helpFormatter.setHeader( "A sample application that demonstrates some of the features of the " "Poco::Util::Application class."); helpFormatter.format(std::cout); } int main(const ArgVec &args) { if (!_helpRequested) { logger().information("Command line:"); std::ostringstream ostr; logger().information(ostr.str()); logger().information("Arguments to main():"); for (const auto &it : args) { logger().information(it); } } return Application::EXIT_OK; } private: bool _helpRequested; }; POCO_APP_MAIN(SampleApp) 

I draw your attention to the fact that the application must contain mechanisms for logging, loading configurations and processing options.
For example, to process options there are:



To configure:



For journaling:



Weakly connected, modules and plugins.


Weak connectivity in the "corporate" system is the possibility of a quick and painless replacement of certain mechanisms. This applies to both modules within the application and the applications themselves, as implementations, for example, microservices. What we have, in terms of C ++. With modules, everything is bad, even though they implement interfaces, but they live inside a "compiled" application, so there will not be a quick substitution, but plug-ins come to the rescue! With the help of dynamic libraries, you can not only organize a quick substitution, but also the simultaneous operation of two different versions. There is a whole world of "remote procedure call" aka RPC. Mechanisms to search for interface implementations coupled with RPC have generated something similar to OSGI from the world of Java. It's great to have support for this in the C ++ ecosystem and it is, here are a couple of examples:


Example of the module for the "application server" POCO OSP


 #include "Poco/OSP/BundleActivator.h" #include "Poco/OSP/BundleContext.h" #include "Poco/ClassLibrary.h" namespace HelloBundle { class BundleActivator: public Poco::OSP::BundleActivator { public: void start(Poco::OSP::BundleContext::Ptr pContext) { pContext->logger().information("Hello, world!"); } void stop(Poco::OSP::BundleContext::Ptr pContext) { pContext->logger().information("Goodbye!"); } }; } // namespace HelloBundle POCO_BEGIN_MANIFEST(Poco::OSP::BundleActivator) POCO_EXPORT_CLASS(HelloBundle::BundleActivator) POCO_END_MANIFEST 

Sample module for Apache Celix "servrer"


 #include "Bar.h" #include "BarActivator.h" using namespace celix::dm; DmActivator* DmActivator::create(DependencyManager& mng) { return new BarActivator(mng); } void BarActivator::init() { std::shared_ptr<Bar> bar = std::shared_ptr<Bar>{new Bar{}}; Properties props; props["meta.info.key"] = "meta.info.value"; Properties cProps; cProps["also.meta.info.key"] = "also.meta.info.value"; this->cExample.handle = bar.get(); this->cExample.method = [](void *handle, int arg1, double arg2, double *out) { Bar* bar = static_cast<Bar*>(handle); return bar->cMethod(arg1, arg2, out); }; mng.createComponent(bar) //using a pointer a instance. Also supported is lazy initialization (default constructor needed) or a rvalue reference (move) .addInterface<IAnotherExample>(IANOTHER_EXAMPLE_VERSION, props) .addCInterface(&this->cExample, EXAMPLE_NAME, EXAMPLE_VERSION, cProps) .setCallbacks(&Bar::init, &Bar::start, &Bar::stop, &Bar::deinit); } 

It should be noted that in distributed weakly connected systems, it is necessary to have mechanisms that ensure transactional operations, but at the current time there is nothing like this for C ++. Those. as there is no possibility to execute a query to the database, file and esb within a single transaction within one application, there is no such possibility for distributed operations. Of course, you can write everything, but there is no generalized something. Someone will say that there is software transactional memory , yes, of course, but this will only facilitate the writing of transactional mechanisms on its own.


Tools


Of all the many auxiliary tools I want to highlight serialization and DSL, because their presence allows you to implement many other components and scenarios.


Serialization


Serialization is the process of translating any data structure into a sequence of bits. The inverse of the serialization operation is the deserialization (structuring) operation — restoring the initial state of the data structure from the wikipedia bit sequence. In the context of C ++, it is important to understand that today there is no possibility of serializing objects and transferring them to another program that previously knew nothing about this object. By object in this case I understand the implementation of a certain class with fields and methods. Therefore, I will highlight two main approaches used in the C ++ world:



In literature and the Internet, the division into binary and text format is often found, but I consider this separation to be not entirely correct, for example, MsgPack does not save information about the type of object, respectively, control is given to the correct display of the programmer and the format MsgPack is binary. Protobuf , on the contrary, saves all the meta information into an intermediate representation, which allows it to be used between different programming languages, while Protobuf is also binary.
So the serialization process, why do we need it. To reveal all the nuances, we need another article, I will try to explain with examples. Serialization allows, while remaining in terms of a programming language, to “package” software entities (classes, structures) for transmission over the network, for persistent storage, for example, in files and other scripts that, without serialization, force us to invent our own protocols, take into account the hardware and software platform, text encoding, etc.
Here are a couple of examples of libraries for serialization:



DSL


Domain Specific Language is a programming language for your subject area. Indeed, when we are engaged in the automation of a company, we are confronted with the customer's subject area and describe all business processes in terms of the subject area, but as soon as it comes to programming, programmers, together with analysts, map the concepts of business processes into concepts framework and programming language. And if the business processes are not a certain number, but the subject area is defined quite strictly, then it makes sense to create your own DSL, for implementing most of the existing scenarios and adding new ones. In the c ++ world, there are not so many possibilities for a “fast” implementation of your DSL. There are, of course, mechanisms for embedding lua, javascript and other programming languages ​​in the C ++ program, but who needs vulnerabilities and a potentially uncontrollable execution engine of "everything"?! So let's look at the tools that DSL can do on its own.



The Boost.Proto library is just created to create your own DSL, this is its direct purpose, here’s an example


 #include <iostream> #include <boost/proto/proto.hpp> #include <boost/typeof/std/ostream.hpp> using namespace boost; proto::terminal< std::ostream & >::type cout_ = { std::cout }; template< typename Expr > void evaluate( Expr const & expr ) { proto::default_context ctx; proto::eval(expr, ctx); } int main() { evaluate( cout_ << "hello" << ',' << " world" ); return 0; } 


Flex and Bison are used to generate lexers and parsers of grammars invented by you. The syntax is not simple, but it solves the problem effectively.
Sample code to generate lexer


 /* scanner for a toy Pascal-like language */ %{ /* need this for the call to atof() below */ #include <math.h> %} DIGIT [0-9] ID [az][a-z0-9]* %% {DIGIT}+ { printf( "An integer: %s (%d)\n", yytext, atoi( yytext ) ); } {DIGIT}+"."{DIGIT}* { printf( "A float: %s (%g)\n", yytext, atof( yytext ) ); } if|then|begin|end|procedure|function { printf( "A keyword: %s\n", yytext ); } {ID} printf( "An identifier: %s\n", yytext ); "+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext ); "{"[^}\n]*"}" /* eat up one-line comments */ [ \t\n]+ /* eat up whitespace */ . printf( "Unrecognized character: %s\n", yytext ); %% main( argc, argv ) int argc; char **argv; { ++argv, --argc; /* skip over program name */ if ( argc > 0 ) yyin = fopen( argv[0], "r" ); else yyin = stdin; yylex(); } 

Also , there is the SCXML - State Chart XML specification: State Machine Notation for Control Abstraction, a description of the state machine in an XML-like markup. This is not exactly DSL, but also a convenient mechanism for automating processes without programming. Qt SCXML has excellent implementation. There are other implementations, but they are not so flexible.
This is an example of an FTP client in SCXML notation, an example is taken from the Qt Documentation website


 <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="FtpClient" datamodel="ecmascript"> <state id="G" initial="I"> <transition event="reply" target="E"/> <transition event="cmd" target="F"/> <state id="I"> <transition event="reply.2xx" target="S"/> </state> <state id="B"> <transition event="cmd.DELE cmd.CWD cmd.CDUP cmd.HELP cmd.NOOP cmd.QUIT cmd.SYST cmd.STAT cmd.RMD cmd.MKD cmd.PWD cmd.PORT" target="W.general"/> <transition event="cmd.APPE cmd.LIST cmd.NLST cmd.REIN cmd.RETR cmd.STOR cmd.STOU" target="W.1xx"/> <transition event="cmd.USER" target="W.user"/> <state id="S"/> <state id="F"/> </state> <state id="W"> <onentry> <send eventexpr="&quot;submit.&quot; + _event.name"> <param name="params" expr="_event.data"/> </send> </onentry> <transition event="reply.2xx" target="S"/> <transition event="reply.4xx reply.5xx" target="F"/> <state id="W.1xx"> <transition event="reply.1xx" target="W.transfer"/> </state> <state id="W.transfer"/> <state id="W.general"/> <state id="W.user"> <transition event="reply.3xx" target="P"/> </state> <state id="W.login"/> </state> <state id="P"> <transition event="cmd.PASS" target="W.login"/> </state> </state> <final id="E"/> </scxml> 

And this is how it looks in the SCXML visualizer.


Ftp-client-scxml


Data Access and Integration


This is perhaps one of the most "pressing" topics in the world with ++. The world of data for a c ++ developer is always connected with the need to be able to display them on the essence of a programming language. A row in a table is in an object or structure, json is in a class, and so on. In the absence of reflection, this is a huge problem, but we, with ++ nicknames, do not despair and find various ways out of the situation. Let's start with the DBMS.


Now I will be trivial, but ODBC is the only universal access mechanism for relational databases, but we haven't come up with other options yet, but with ++ the community is not in place and today there are libraries and frameworks providing generic access interfaces to several DBMS.
First of all, I will mention libraries that provide unified access to the DBMS using client libraries and SQL



All of them are good, but they make you remember the nuances of displaying data from the database into objects and structures with ++, plus the efficiency of SQL queries falls right on your shoulders.
The following examples are ORM in C ++. Yes there are such! And by the way, SOCI, supports ORM mechanisms, through the specialization soci :: type_conversion, but I deliberately did not include it, since this is not its direct purpose.



 #pragma db object class person { // ... private: friend class odb::access; person () {} #pragma db id string email_; string name_; unsigned short age_; }; // ... odb::sqlite::database db ("people.db"); person john ("john@doe.org", "John Doe", 31); odb::transaction t (db.begin ()); db.persist (john); typedef odb::query<person> person_query; for (person& p: db.query<person> (person_query::age < 30)); cerr << p << endl; jane.age (jane.age () + 1); db.update (jane); t.commit (); 


Although integration via DBMS is considered "integration", I would prefer to leave it behind the brackets and move on to integration through protocols and APIs.



Networking


I deliberately left everything connected directly with the network interaction last, because In this field, C ++ developers have the least problems in my opinion. It remains only to choose the pattern that is closest to your decision, and the framework that implements it. Before listing the most popular libraries, I want to point out an important detail in developing my own network applications. If you decide to invent your own protocol over TCP or UDP, be prepared that all sorts of "smart" security tools will block your traffic, so take care to package your protocol, for example, in https or there may be problems. So, libraries:



Total


So, what I wanted to say is the review of these libraries. If I succeeded, then you left the impression that the "enterprise edition", as it were, is there, but there are no solutions for its implementation and use, only the zoo of libraries. So it really is. There are more or less holistic libraries for developing applications for the corporate segment, but there is no standard solution. From myself I can only recommend pocoproject and maccina.io as a starting point in researching backend and boost solutions for any cases, and of course I am looking for like-minded people to promote the concept of "C ++ Enterprise Edition"!


')

Source: https://habr.com/ru/post/455726/


All Articles