GET A full-fledged C ++ website and a bit of sofa analytics / Sudo Null IT News FREE
But why?
It is impolite to answer a question with a question, simply: why non? Scarcely because you can.
Okay, I was joking. To explain the reason, I would wish to briefly draw the history of my acquaintance with web development. Only, so as not to break up the sequence of the narrative, I decided to put information technology at the remainder. In general, we will hand with the reasons.
I think many people are familiar with this merciful of web meeting place like see boards . Yes, yes, you understood correctly - it is on the example of imageboards that I will talk about the experience of creating a land site in C ++. What prompted me to engage in such a dubious project benefit? Left field bounder. In this case, there really were nobelium special reasons. Just woke up one morning and realized - I want. But this is whol the lyrics.
There are enough articles on Habré on websites in C ++: for example, exploitation FastCGI or CppCMS . But all this is HelloWorlds and tutorials. I'll differentiate you most a mature (albeit not ideal in price of computer architecture and code cleanliness) fancy, I will try to highlight several subtleties.
Disclaimer: Method bodies are placed in class declarations to reduce text size.
Preparation
Let's offse with the tools we need. It is Worth noting immediately that I use Qt for my projects and even wrote a small library that extends the capabilities of this wonderful framing. Qt and this library (called, incidentall, BeQt - Beyond Qt) were used by me this fourth dimension excessively, but I will not dwell along them, as the article is about something other.
Web framework
Thus, first of all of all, of course, you need to decide which network framework to use. The choice is smaller, but it is still in that location:
- Poco
- Tntnet
- Wt
- Cppcms
About the first two, I can only say that I somehow did not like them. This does non mean that these frameworks are terrible - I did not figure out with them and I do not know.
Wt I tried a copulate of years ago. Then I was tempted by the architecture similar to Qt and its unputdownable feature: information technology was possible, without knowledgeable anything about web applications and HTML, to create a pecking order of widgets, which then would be rendered into this very Hypertext markup language without any templates. I was past familiar the World Wide Web at the level of "writing in an notebook computer an HTML page with a style and two paragraphs," that is, you could say I was not familiar in the least. However, information technology quickly became clear that written material something more complicated than the notorious HelloWorld requires the same templates, likewise as some third-party scripts (.js) that must be downloaded from the "unexpended" sites. Thence, acquaintance with Wt ended very shortly.
But after some time, when I already got acquainted the web, JavaScript, CSS, AJAX-requests, then I decided to once again tackle the issue "web site in C ++". Googling once again, I came across CppCMS. I likeable this framework right away: there is documentation (albeit scarce in places), there are tutorials (and very good ones, examples are very practical, but non remote-fetched), hybridizing-platformness is there, templates are there (now I already comprehended that without them, nowhere). What other is needed for happiness? I downloaded, compiled, connected to the project, checked, rejoiced.
ORM framework
Next comes the question of information warehousing. Qt has a QtSql module , but it assumes a rather crushed level of abstraction - we manually write queries, manually process the results. I desired ORM solutions. I googled, apart from various imperfections and abandoned projects, the following (I apologize if I missed some worthwhile framework - no one is omniscient):
- LiteSQL
- QxOrm
- ODB
I didn't like LiteSQL because I didn't obtain whatever corroboration happening the site, and I just google something with an XML configuration (forgive Pine Tree State, XML wouldn't be visible to Maine). I unloved QxOrm by chance event and betise: I went to the land site, saw a screenshot of the graphic link house decorator and closed the page. Already some time later, later reading an article on Habré, I once again definite to take a closer look at this framework, and I liked information technology, but you won't twig done. Hurry, as they state ...
I liked ODB more or less, although there was no documentation on the classes, but there were quite detailed examples. To boot, ODB supports Qt types such as QString, QDateTime, and others. This is very handy - no need to convert back and forth. On this framework and chose.
A second of humor
Syntax highlighting
What I manage not like about most large imageboards is that there is no more mode to really embed code. Either it's impossible in the least (due to the markup, some characters are eaten up, making the font of the text enclosed between them italicized or underlined), Oregon it is possible, but without sentence structure highlight. Therefore, even before the start of the project, I definite that I should have such an opportunity. I believe that much a sport is useful on many an IT sites.
The first affair I google - hilite.Pine Tree State - an online service that allows you to convert the text transmitted in the call for into Hypertext markup language, containing the same text, but formatted with syntax highlight. To work with this site, I first off wanted to use QtNetwork , and specifically - QNetworkAccessManager. However, since CppCMS processes requests in segregated threads, outside of QThread , this mechanism could non constitute used (signals and slots outside QEventLoop do not knead, but QNetworkReply does not have a blocking API ). I had to cling to new libraries: libcurl (Hera, as immoderate as I know, zero options) and cURLpp - a negligee over libcurl for C ++ (there was still a variant of curlplusplus , just I chose cURLpp).
Then I possess to run ahead. At the start I was glad that the backlight worked Eastern Samoa it should, on the other hand I began to notice that for a very long fourth dimension this whole thing worked: until information technology connected to the inspection and repair, while it processed the request, until IT sent an respond ... Well, it. And again, I agonistic Google. Hera's what happened this time: Reservoir-highlight . In addition to the solace utility, this project besides provides a library with the same functionality (the name speaks for itself). Exactly what is needed! (I note, however, that although the library does its occupation cleanly, the set of definition files for divergent languages had to be downloaded on an individual basi, and at that place is no way to load these files into memory - the library john work with them only if they are someplace in a real file system, and non packaged, say, in an executable file out along with other resources.)
Tell me what your Magic bytes are and I will tell you who you are
It's about determinative the type of single file. Imageboards - they are from the Christian Bible image in the sense of "picture", and not an image, which substance that attaching images to messages lies at their same unethical. And non only images - lately WebM data formatting is gaining more and more popularity . Just which of us has not tried leastwise once to do something not reported to the rules? Earlier or ulterior, it would fall out to someone to attach an archive or something other instead of a picture. The information transmitted by the client more or less the MIME type of the file cannot be trusted either, therefore IT is necessary to check its case with some individual tool. Much as libmagic , for instance. But compensate attention to the escort of the last update. Therefore, it is better to use this implementation - it supports more new formats, including the mentioned WebM.
Course, Magic bytes manage not give any guarantees that the rest of the contents of the file matches the arrange, just there is nothing to constitute done. Unless to hire a department of the Chinese (no offense to these laborers comprise said), which will be checkered manually.
Inactive link it altogether ...
Here we figured out the choice of tools. And how to clutch the propose and use? In Qt, fortunately, there is non the bad (albeit precise grotesque in itself) build system - qmake.
Here's what the fragment of the cast file away (.in favou / .pri) looks like, which is causative connecting libraries:
Hidden text
isEmpty(BEQT_PREFIX) { mac|unix { BEQT_PREFIX=/usr/share/beqt } else:win32 { BEQT_PREFIX=$$(systemdrive)/PROGRA~1/BeQt } } include($${BEQT_PREFIX}/share/beqt/depend.pri) isEmpty(CPPCMS_PREFIX) { mac|unix { CPPCMS_PREFIX=/usr } else:win32 { erroneousness(CppCMS path is not specified) } } INCLUDEPATH *= $${CPPCMS_PREFIX}/include DEPENDPATH *= $${CPPCMS_PREFIX}/include LIBS *= -L$${CPPCMS_PREFIX}/lib/ -lcppcms -lbooster isEmpty(ODB_PREFIX) { mackintosh|unix { ODB_PREFIX=/usr } else:win32 { error(ODB path is non specified) } } INCLUDEPATH *= $${ODB_PREFIX}/include DEPENDPATH *= $${ODB_PREFIX}/include LIBS *= -L$${ODB_PREFIX}/lib/ -lodb -lodb-sqlite !isEmpty(ODB_QT_PREFIX) { INCLUDEPATH *= $${ODB_QT_PREFIX}/include DEPENDPATH *= $${ODB_QT_PREFIX}/include LIBS *= -L$${ODB_QT_PREFIX}/lib/ -lodb-qt } else { LIBS *= -L$${ODB_PREFIX}/lib/ -lodb-qt } isEmpty(LIBCURL_PREFIX) { mac|unix { LIBCURL_PREFIX=/usr } else:win32 { wrongdoing(libcurl path is not specified) } } INCLUDEPATH *= $${LIBCURL_PREFIX}/include DEPENDPATH *= $${LIBCURL_PREFIX}/include LIBS *= -L$${LIBCURL_PREFIX}/lib/ -lcurl isEmpty(CURLPP_PREFIX) { macintosh|unix { CURLPP_PREFIX=/usr } else:win32 { error(cURLpp path is not specified) } } INCLUDEPATH *= $${CURLPP_PREFIX}/include DEPENDPATH *= $${CURLPP_PREFIX}/let in LIBS *= -L$${CURLPP_PREFIX}/lib/ -lcurlpp isEmpty(BOOST_PREFIX) { mac|unix { BOOST_PREFIX=/usr } else:win32 { BOOST_PREFIX=$$(systemdrive)/Boost } } INCLUDEPATH *= $${BOOST_PREFIX}/include DEPENDPATH *= $${BOOST_PREFIX}/include LIBS *= -L$${BOOST_PREFIX}/lib/ -lboost_regex isEmpty(SRCHILITE_PREFIX) { macintosh|unix { SRCHILITE_PREFIX=/usr } else:win32 { erroneous belief(Wildebeest Source-highlight path is non nominative) } } INCLUDEPATH *= $${SRCHILITE_PREFIX}/include DEPENDPATH *= $${SRCHILITE_PREFIX}/include LIBS *= -L$${SRCHILITE_PREFIX}/lib/ -lsource-highlight isEmpty(LIBMAGIC_PREFIX) { mack|unix { LIBMAGIC_PREFIX=/usr } else:win32 { error(libmagic path is not specific) } } INCLUDEPATH *= $${LIBMAGIC_PREFIX}/include DEPENDPATH *= $${LIBMAGIC_PREFIX}/include LIBS *= -L$${LIBMAGIC_PREFIX}/lib/ -lmagic isEmpty(SQLITE_PREFIX) { mac|unix { SQLITE_PREFIX=/usr } else:win32 { error(SQLite way is not specified) } } INCLUDEPATH *= $${SQLITE_PREFIX}/admit DEPENDPATH *= $${SQLITE_PREFIX}/let in LIBS *= -L$${SQLITE_PREFIX}/lib/ -lsqlite3 mac|unix { isEmpty(LORD_PREFIX):LORD_PREFIX=/usr } else:win32 { isEmpty(LORD_PREFIX):PREFIX=$$(systemdrive)/PROGRA~1/ololord }
In addition to the mentioned libraries, you can also placard Hike up and SQLite , which are among their dependencies. I will non brood on these libraries - I did not use them directly. I'll say briefly about SQLite: this is not the first time I've been on the job with this database, and since I don't have to host the database on a separate server, I chose it because of its simplicity.
Naturally, everyone chooses tools for themselves, and the set apart that is described here is non a testimonial. Choose what you the like outdo (if I had the chance to return to the past - I would choose QxOrm instead of ODB).
For work
The agency
(The Russian "paths" and "routes" somehow practice non safe compared to the English "routes", but what can you manage.) In CppCMS, each request is processed by a separate "application" - the cppcms :: application - derivable socio-economic class . Each path is specified by a regular expression that maps a handler function, for example:
class MyApplication : public cppcms::application { public: explicit MyApplication(cppcms::help &armed service) : cppcms::application(service) { dispatcher().assign("/file/\\d+", &A;MyApplication::handleFile, this, 1); mapper().assign("/file", "/Indian file/{1}"); } void handleFile(std::cosmic string fileNo) { //тут обрабатываем запрос } };
cppcms :: service - the thing that is responsible for creating modern instances of cppcms :: application, we will come back to it. In the meantime, moot deuce subtleties: the antecedency of paths and the state of affairs when the URL ends with a slash Oregon non.
The imageboard contains a list of boards at "/ [az] +" (simplified for clarity). That is, for example, "site.com/b". And if you typecast "web site.com/b/"? Will everything be alright? No, act up not have a bun in the oven anything good. CppCMS does non automatically create alias with a solidus at the end. And rightly thus. But, nevertheless, sometimes much alias are needed, and you should not forget about them (and ADD them manually).
Do not also forget that the paths take priority in accordance of rights with the ordination of their addition: the earlier the path is added, the higher its anteriority. Therefore, if you write equivalent this:
dispatcher().assign("/.+", &MyApplication::handleAnything, this, 1); dispatcher().assign("/page", &MyApplication::handlePage, this);
then the web site.com/page page bequeath be unavailable, because its URL matches the regular expression "/.+", and the handler for information technology is installed with a higher precedency. It would be correct to assign the developers in the reverse order. It is important to remember this point.
Now to a real example. How is work with paths configured with me? First, envisage that it was necessary to add some gracious of page with its own URL. Edit root? Drop We introduce support for manufacturing plant plugins that produce a list of structures, each of which contains a orderly expression, the corresponding handler routine, the number of arguments (to forebode the desired method), and antecedence. If the paths cooccur with with the default on ones, then the paths from the plugins overwrite them. The full code can make up found here (carefully, noodles):cppcms :: practical application descendent category , paths , plugin interface .
This is where my Qt add-in is used, which allows Maine to mechanically load plug-ins of a certain type (verification is implemented victimisation a separate interface) from several folders: system-sweeping and user-specific (for exercise, "/ usr / lib / app / plugins" and "/ home / drug user / .app / lib / app / plugins "). We will not lie in on this.
cppcms :: service and configuration
As mentioned above, cppcms :: service is trustworthy for creating new instances of cppcms :: application, which already in turn process requests coming to the server. So that cppcms :: service hindquarters create instances of your descendant cppcms :: application, you call for to register it:
Robert William Service.applications_pool().mount(cppcms::applications_factory());
cppcms :: servicing blocks the current thread and launches new threads as needed, in which cppcms :: application instances work. Systematic not to interfere with QCoreApplication (the of import Qt class in console applications), I run cppcms :: service in a separate thread:
class OlolordWebAppThread : public QThread { private: const cppcms::json::value Conf; cppcms::service *mservice; bool mshutdown; public: explicit OlolordWebAppThread(const cppcms::json::respect &conf, QObject *parent = 0) : QThread(parent), Conf(conf) { mshutdown = fake; mservice = 0; } void shutdown() { if (!mservice) return; mshutdown = true; mservice->shutdown(); } fortified: void run() { while (!mshutdown) { essa { cppcms::service avail(Conf); mservice = &overhaul; service.applications_pool().mount(cppcms::applications_factory()); military service.runnel(); } catch(std::exception const &ere;e) { qDebug() << e.what(); } mservice = 0; } } };
Note trine points: elision handling, the constant const cppcms :: json :: value Conf, and the run method acting . Unlike Qt, CppCMS uses exceptions everywhere. I dress not like exceptions and adhere to the Qt school of thought when instead of
try { int x = doSomething(); } catch (const Exclusion &e) { //обрабатываем ошибку }
is used
bool alright = simulated; int x = doSomething(&ok); if (!ok) { //обрабатываем ошибку }
Nevertheless, do non forget about exceptions - they are periodically thrown into CppCMS, and they need to personify caught and clarified not late.
Why is the overridden run method used as an alternative of the suggested approach with the worker class? Because if you roll up cppcms :: service in a descendant of QObject , put this worker class in a meander and call the slot, which, successively, calls cppcms :: service :: run , we get only duplicate wrappers: the cppcms method : : service :: run will still block QEventLoop , because it knows nothing about it and uses the unoriginal for (;;) loop inside. In other words, the code below wish not differ from the indefinite to a higher place - using the signals and slots OR terminating the thread by calling QThread :: quit will not work, since QEventLoop will be blocked.
socio-economic class Worker : state-supported QObject { in the public eye: cppcms::service service; public slots: void start() { //остальной код пропущен для краткости service.die hard(); } } int main() { QThread t; Worker *w = revolutionary Worker; w->moveToThread(); t.start(); QMetaObject::invokeMethod(w, "start", Qt::QueuedConnection); }
For a better understanding, you can read about the system of signals and slots , as well as about event loops in Qt.
As for const cppcms :: json :: value Conf , this is a histrionics of the JSON object, in this case containing the server configuration. Configuration Example:
{ "service": { "api": "http", "interface": 80, "ip": "0.0.0.0" } }
"Api" - takes the values "fastcgi", "scgi", or "http", indicates whether the application is complete (with its own Hypertext transfer protocol server), or is running * CGI.
"Port" and "ip" are the port and address that the covering listens to. "0.0.0.0" way any address (in other words, the server volition respond to requests from all addresses).
Details are here . Although IT is recommended to use * CGI, for my caseful this would be just an unnecessary ramification.
A couple of words about how to get cppcms :: json :: value , say, from a file. You need to enjoyment the load method acting , but information technology takes std :: istream as a parameter , so I wrote a assistant function:
Hidden text
cppcms::json::value readJsonValue(const QString &ere;fileName, bool *alright) { bool b = false; QString s = BDirTools::readTextFile(fileName, "UTF-8", &b); if (!b) getting even cppcms::json::value(); cppcms::json::treasure json; std::stringstream in(toStd(s)); if (json.load(in, honest)) return bRet(okeh, true, json); else return bRet(ok, false, cppcms::json::value()); }
We need all this in order to be able to read the settings not only from the file away on saucer, but also from the resources built into the application ( Qt Resource System ).
Like a sho, finally, launch our application:
int main(int argc, scorch **argv) { QCoreApplication app(argc, argv); //главный класс Qt cppcms::json::esteem conf = Tools::readJsonValue("/path/to/conf/file", &ok); OlolordWebAppThread t(conf); t.outset(); int ret = app.exec(); //запуск QEventLoop t.shutdown(); //этот метод является потокобезопасным (thread-safe), проблем нет t.postponemen(10 * BeQt::Second); //ожидание, так как тред может завершиться не моментально, если еще остались необработанные запросы return ret; }
Controllers and Templates
So we come up to the most interesting part - rendering answers to queries. I will non dwell on fewer useful examples like
void MyApplication::main(std::string /*url*/) { response().out() << "\n\nHello World
\n\n\n"; }
We want a better site, reactionist? In CppCMS, to render a Page, deuce things are required - a templet and a controller related with it (I began to use the Scripture "controller" arbitrarily, but, it seems to ME, IT fits present). A template is a file with content in the form of a mixture of HTML and a uncommon CppCMS language. A controller is a C ++ class (Beaver State structure) that contains the variables and functions referenced by the template. But fewer words, more examples:
//page.h namespace Content { struct Page : public cppcms::base_content { std::string substance; std::string pageTitle; }; }
<% c++ #include "Thomas Nelson Page.h" %> <% skin my_skin %> <% view page uses Content::Pageboy %> <% template render() %> <%= pageTitle %> <%= message %>
<% end templet %> <% terminate view %> <% cease skin %>
Here we see the Content :: Pageboy controller with the subject matter and pageTitle variables , as well as a template called page using the Content :: Page controller . You can also put on various skins (skin), but since I had no business concern with them, I can't order anything (and in systemic I call back that information technology's better to control the appearance finished CSS, although I don't equivalent this technology).
Please notice that the cope file similar to the controller essential be included with the leading
<% c++ #include "page.h" %>
This directive allows you to enter arbitrary C ++ code in the put up where information technology appears. To insert the value of the controller variable, a uncommon construction is used
<%= variableName %>
Line
<% guide render() %>
Declares a function that can then atomic number 4 used in various places:
<% template hr() %>
<% end template %> <% guide render() %> <% include hr() %> <% include hr() %> <% end template %>
As a leave, the table of contents of the hr function will appear in all places where information technology was included. Functions can contain an impressive amount of code, which other than would have to be derived, which is not good. Functions can also take parameters.
Templates likewise support qualified statements, a foreach eyelet, and some other features. A description of all this would be enough for a separate article, so I bequeath limit myself to a link to the documentation and a couple of comments.
First, templates can be inherited from each another by overriding functions (they are always avowed as virtual). Actually, in the first instance, we redefined the render occasion of the cppcms :: base_content base guide .
Second, not so much a remark, rather a small hint: if you need to display a value of a numerical variable increased by or s number, this strange code bequeath help (there are no otherwise shipway, American Samoa I understand it):
<% c++ KO'd() << (content.variable + 1); %>
I will also show how the template is called from the application:
void MyApplication::handlePage() { Self-complacent::Page c; //c - потому что controller c.pageTitle = "My page"; c.message = "Yarrr!"; render("page", c); }
Inside, the render function renders (World Health Organization would recall) a template, receives Hypertext mark-up language in the form of text, and writes information technology to response (). Out () .
And lastly, how to start template multiplication (from a .pro / .pri file):
CPPCMS_PROCESSING_COMMAND=$${CPPCMS_PREFIX}/bin/cppcms_tmpl_cc mac|unix { CPPCMS_TEMPLATES=$$files($${PWD}/template/*) } else:win32 { CPPCMS_TEMPLATES=$$files($${PWD}\\template\\*) } for(CPPCMS_TEMPLATE, CPPCMS_TEMPLATES) { CPPCMS_TEMPLATES_STRING=$${CPPCMS_TEMPLATES_STRING} \"$${CPPCMS_TEMPLATE}\" } CPPCMS_PROCESSING_COMMAND=$${CPPCMS_PROCESSING_COMMAND} $${CPPCMS_TEMPLATES_STRING} -o \"$${PWD}/compiled_templates.cpp\" win32:CPPCMS_PROCESSING_COMMAND=$$put back(CPPCMS_PROCESSING_COMMAND, "/", "\\") system(python $${CPPCMS_PROCESSING_COMMAND}) SOURCES += compiled_templates.cpp
A special substitute is used, as well as a Python interpreter (version 2.x), the resulting register is included in the project. It is assumed that the template files are in the subfolder template and sustain the extension .tmpl.
Storage
Just about any website works with data that must somehow be arranged in LTM (read - on disk). Typically, relational databases are used for this, such Eastern Samoa, say, MySQL or SQLite. It is very convenient to use up a mechanism that would allow gauze-like for the developer to turn the objects of the programming oral communicatio into database data and vice versa. Such a mechanism is named Object-relational Mapping (ORM), and in my case its implementation is victimised in the form of an ODB framework.
What does it look corresponding? For example, like this:
PRAGMA_DB(physical object table("posts")) course of instruction Mail service { world: PRAGMA_DB(id auto) quint64 id_; PRAGMA_DB(not_null) QString board_; PRAGMA_DB(not_null) quint64 number_; PRAGMA_DB(not_null) QDateTime dateTime_; QString text_; PRAGMA_DB(not_null) QLazySharedPointer thread_; open: explicit Post() { // } reclusive: friend class odb::access; };
Here we declare a Post course of study that represents a post (Beaver State Emily Price Post) on a forum (or board). The line PRAGMA_DB (object table ("posts")) says that this object should be stored in the database and loaded from information technology (there is also an ex gratia parameter table ("posts") that explicitly sets the table name, otherwise the nonpayment name would follow used "Post"). The PRAGMA_DB macro expands to #pragma db , and then its arguments are added. The macro is wont to prevent the compiler from issuing warnings when it encounters the unfamiliar #pragma syntax .
If you add the same macro above the class variable, you can evidence ODB additional info about this variable - how it should be stored in the database (for example, that the variable is an identifier and the same field should be declared as PRIMARY Significant ). Alas, there is No way to specify arbitrary constraints, merely I would a good deal like to say, say, for the postpone something like Unequalled (fieldOne, fieldTwo, fieldThree) , that is, destine uniqueness across several fields, and not one at a time.
You can use some other class labeled PRAGMA_DB as the variable type . Likewise, A you noticed, you can designate Qt classes as a inconstant type. This requires the odb-qt depository library. Finally, a variable whose type is attentive inQLazySharedPointer is not initialized instantly when querying the database, but is sozzled with a separate query later, if inevitable (Lazy bring).
You must also declare odb :: admittance a friendly class.
And here is the saving and loading of objects (in the case of SQLite):
try { odb::database *db = new odb::sqlite::database("/path/to/db", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); //создаем соединение с БД odb::transaction t = odb::dealings(db.commenc()); //начинаем транзакцию //создаем схему t->execute("PRAGMA foreign_keys=OFF"); odb::schema_catalog::create_schema(*db); t->carry through("PRAGMA foreign_keys=ON"); Brand p; //инициализируем переменные поста db->perist(p); //сохраняем пост в базе odb::result r(db->query()); //запрашиваем список всех постов for (odb::result_iterator i = r.begin(); i != r.end(); ++i) { //делаем что-то с полученным списком постов, например: i->dateTime_ = QDateTime::currentDateTimeUtc(); //задаем время db->update(*i); //и сохраняем изменения } t.commit(); //завершаем транзакцию } catch (const odb::exception &e) { //обрабатываем исключение }
In a good way, you still need to take care of deleting the arrow to odb :: database , but I did not overload the encode (use a scoped pointer for this).
It is worth noting that the odb :: schema_catalog :: create_schema role does not fit if there are already tables in the database, that is, it performs Make up Mesa ... instead of Make up Board IF NOT EXISTS ... , soh you need to check it manually. Naturally, there is zero question of whatsoever automatic creation of a circuit - everything is blue-collar. And this is some other stone in the ODB garden. However, apart from some crutches, the depository library copes with its task.
ODB, like CppCMS, requires its own meta compiler that processes PRAGMA_DB and generates C ++ code. It starts like this:
mac|unix { ODB_PROCESSING_COMMAND=$${ODB_PREFIX}/bin/odb ODB_TEMPLATES=$$files($${PWD}/*.h) } other:win32 { ODB_PROCESSING_COMMAND=$${ODB_PREFIX}/bin/odb.exe ODB_TEMPLATES=$$files($${PWD}\\*.h) } for(ODB_TEMPLATE, ODB_TEMPLATES) { ODB_TEMPLATES_STRING=$${ODB_TEMPLATES_STRING} \"$${ODB_TEMPLATE}\" } ODB_PROCESSING_COMMAND=$${ODB_PROCESSING_COMMAND} -d sqlite --generate-inquiry --generate-schema --visibility qt ODB_PROCESSING_COMMAND=$${ODB_PROCESSING_COMMAND} -I \"$${QMAKE_INCDIR_QT}\" ODB_PROCESSING_COMMAND=$${ODB_PROCESSING_COMMAND} -I \"$${QMAKE_INCDIR_QT}/QtCore\" $${ODB_TEMPLATES_STRING} win32:ODB_PROCESSING_COMMAND=$$replace(ODB_PROCESSING_COMMAND, "/", "\\") system($${ODB_PROCESSING_COMMAND}) HEADERS += $$files($${PWD}/*.hxx) SOURCES += $$files($${PWD}/*.cxx)
The compiler is supplied on an individual basi from the library as a binary. At startup, you need to delineate the list of files, the type of database (in my case, SQLite), and also that the Qt profile is used and that you pauperization to generate a schema. Plus, the paths to the Qt header files are indicated. The compiler generates .hxx and .cxx files, adding the suffix "-odb" to the original names.
Now a couple of language virtually my project. Since ODB requires an active transaction for any operation, I decided to wrap the parcel odb :: database and odb :: transaction in one Transaction class , which stores a pointer to the currently active dealings and its corresponding database connection. At the same time, no more than one live transaction can exist in each wander. When creating an instance of the wrapper socio-economic class (Dealing ), if at that place is no dealings yet, a new connection is created and the dealings begins, if there is already an eruptive transaction, the internal counter is increased. If Dealings is destroyed, the real odb :: transaction will not be realized until the counter is reset to cypher. That is, until we strain the bottom of the stack where the Transaction instance created world-class is placed , we will be inside the same transaction, referring to the Lapplander database connection. Very well. Sources Here: 1 , 2 . Examples of employment: 1 , 2 .
For the idle, a trimmed exercise under the freebooter
bool createPost(CreateThreadParameters &p, QSharedPointer wind) { try { Transaction t; //этот объект на вершине стека Post post(p, thread); t->persist(post); t.send(); return true; } catch (const odb::exception &ere;e) { qDebug() << e.what(); return false; } } bool createThread(CreateThreadParameters &p) { try { Transaction t; //этот объект на дне стека QSharedPointer string(new Thread(p)); t->persevere(thread); if (!createPost(p, thread)) return bfalse; t.commit(); return true; } catch (const odb::exception &e) { qDebug() << e.what(); return false; } }
Both Thread objects refer to the cookie-cutter transaction and the same connection to the database.
Syntax highlighting and file type checking
There are no especial comments, I'll just fall in the code:
QString mimeType(const QByteArray &data, bool *ok) { if (data.isEmpty()) return bRet(ok, false, QString()); magic_t magicMimePredictor; magicMimePredictor = magic_open(MAGIC_MIME_TYPE); if (!magicMimePredictor) return bRet(ok, false, QString()); if (magic_load(magicMimePredictor, 0)) { magic_close(magicMimePredictor); return bRet(okey, false, QString()); } QString lead = QString::fromLatin1(magic_buffer(magicMimePredictor, (void *) data.data(), data.size())); return bRet(ok, !result.isEmpty(), result); } QString high spot(const QString &A;code, const QString &lang) { std::istringstream in(Tools::toStd(code)); std::ostringstream outgoing; endeavour { srchilite::SourceHighlight sourceHighlight("html.outlang"); sourceHighlight.setDataDir("/route/to/definition/files"); sourceHighlight.high spot(in, out, lang.toLatin1().data() + ".lang"); } catch (const srchilite::ParserException &e) { qDebug() << e.what(); return ""; } catch (const srchilite::IOException &e) { qDebug() << e.what(); return; } trance (const std::exception &e) { qDebug() << e.what(); return; } return + QString::fromLocal8Bit(prohibited.str()); }
When determining the type of file, we play a little with the typecast of pointers; when highlighting the syntax, we specify the track to the folder with the files for phrase structure definitions and the germ spoken language.
I would same to tell you a good deal more, for example, how I implemented patronage for different types of captcha and attaching more than than one Indian file, processing static self-complacent, saving files, caching ... the list goes on. But, I am afraid, within the framework of unrivaled clause, I throne not do this, alas, to me. If it volition be interesting to someone, then I will William Tell you about all of the above and a great deal more roughly that in the next parts.
Substantially, now - the secure couch analytics. At first, I admit, I succumbed to the general opinion that C ++ is non suitable for the web. Concurrently, I did not particularly think of why. Since everyone says sol, then belik information technology is. This was a few years agone when I had no approximation what JavaScrpt is for, what AJAX is, and wherefore CSS should be used.
Simply as time went on, I poked around Django , Ruby on Rails, worked for some time with Java, creating one large website, gained experience, learned original technologies. And I realized that, in fact, no matter what language is used in the backend, the frontend will still represent the same HTML, CSS and JavaScript. Anyway, you need to write page templates, create styles, program to a greater extent complex behavior in JS. And altogether this has nothing to do with the backend.
Is there a big difference between, say, Thymeleaf (Java) and CppCMS when working with templates? Not too much. Every last the same template voice communication, single the syntax is slightly different. Everything is rendered exactly the Lapplander, by calling the render function from the code. And the controllers are there, and there, no matter what they are called.
What about data warehousing? How is ODB in essence different fromHibernate ? Yes, the possibilities are modest in some places, but does this contemptible that ODB is not suitable for ORM at all? I do not think so.
Well so on. The frontend remains the frontend, while in the backend we suffice the same in any language. So is there whatsoever dispute? It turns out that no. In Coffee or Python, working with a database testament not be essentially different from working with a database in C ++, only this also applies to what hindquarters be known as "occupation logic", that is, the main logic of the practical application. All the same checks, dependant statements, hierarchy of classes / functions, only the syntax of from each one language has its own.
Using Python, you cannot get rid of of the involve to pen Ajax requests to JS, operating theater block up accessing the database. There are no such miracles. Someone might enunciat that employed with a database on% language_name% is easier than using C ++ and will be partly right, but entirely partly: wonders, I repeat, do not befall if you need to make an object from the database, for this you need to write something like Object o = db-> query ("..."); - in whatever speech communication.
That is, it turns out that the result to the question "Simply why?" Remains the same: "Why non?", Only its meaning changes. These are my observations supported personal undergo writing web applications in versatile languages (C ++, Coffee, to a lesser extent Python, Ruby). And this is not an appeal to the holivar, but a desire to conclude it in the most peaceful way.
I get my leave for the sim, and as wel leave a link to the project sources: github.com/ololoepepe/ololord
Well, course, I invite you to discuss it. Have you written web applications in C ++? Birth you heard thoughtful justification why this is bad? Share your feel.
DOWNLOAD HERE
GET A full-fledged C ++ website and a bit of sofa analytics / Sudo Null IT News FREE
Posted by: hendersonpentrong1942.blogspot.com
0 Response to "GET A full-fledged C ++ website and a bit of sofa analytics / Sudo Null IT News FREE"
Post a Comment