Le 26/06/2016 21:17, Richard Heck a écrit :
It would help me if you could explain this new syntax. I'm a philosopher, not a 
programmer.

-        theApp()->registerSocketCallback(fd,
-            bind(&LyXComm::read_ready, this));
+        theApp()->registerSocketCallback(fd, [this]() { read_ready(); });



Hi Richard

Thank you for asking. (I think you forgot to send the above one to the
list.)

In C++11, the syntax [X](Y){Z} is called a “lambda expression” and
denotes an anonymous function. The name and the concept is directly
taken from the λ-calculus, which may already be familiar to
philosophers, linguists, and mathematicians in the room (and of course
to computer scientists). Interestingly, the first name to come up in
“The Lambda Calculus”¹, Stanford Encyclopedia of Philosophy,
Section 3: “Brief history of λ-calculus” is Frege.
(¹<http://plato.stanford.edu/entries/lambda-calculus/>)

The (Y) is the list of arguments, with which you are already familiar in
function declarations, e.g. (std::string const & str). The {Z} is what
you would write in the body of such function (including the return
statement). It goes without saying that in the end, this is much
more readable and expressive than creating anonymous functions with
std::bind.

The power of the lambda expressions comes from the [X] parameter. Inside
Z, you can refer and access to any variable that you can already access
and refer to in the scope in which you write the lambda expression. For
instance, I wrote above "read_ready();" because I am essentially in the
scope of a member function of LyXComm, and therefore I can refer to (and
omit) "this->".

Now, the expression {Z} can be evaluated much later, and therefore all
the local variables it refers to may already have been destroyed.
Therefore we need the parameter [X] to indicate how the free variables
appearing in {Z} but not in (Y) are stored when creating the anonymous
function ("captured", in C++-speak). They are either stored by copying
or stored by reference. [X] is a list of variables possibly prefixed by
&, denoting a reference. For instance [x,&y,z] means that x and z are
copied and stored with the anonymous function on creation, whereas y is
stored as a reference to the original object.

In the example above, I refer (implicitly) to "this" and therefore I
must indicate that this (the pointer) is to be copied.

There are two shorthand syntaxes for [X]: [=,...] and [&,...]. =
indicates that all remaining free variables must be copied. & indicates
that all remaining variables must be stored by reference. (In both cases
the this pointer will be copied, not stored by reference.)


+namespace {
+
+template<typename F>
+typename std::result_of<F()>::type call(F f) {
+#if EXPORT_in_THREAD
+    return call_in_gui_thread(f);
+#else
+    return f();
+#endif
+}
+
+} // anon namespace
+
+
  void noAppDialog(QString const & title, QString const & msg, 
QMessageBox::Icon mode)
  {
      int argc = 1;
@@ -136,13 +150,10 @@ int prompt(docstring const & title0, docstring const & 
question,
            docstring const & b1, docstring const & b2,
            docstring const & b3, docstring const & b4)
  {
-#ifdef EXPORT_in_THREAD
-    return InGuiThread<int>().call(&doPrompt,
-#else
-    return doPrompt(
-#endif
-                title0, question, default_button,
-                cancel_button, b1, b2, b3, b4);
+    return call([&, default_button, cancel_button] () {
+            return doPrompt(title0, question, default_button,
+                            cancel_button, b1, b2, b3, b4);
+        });
  }



In the above example, I first added a function call_in_gui_thread in
InGuiThread, which takes a function with signature R(), executes it in
the GUI thread, and returns the result of type R. This is all that we
need, now that anonymous functions of type R() are easy to create,
thanks to lambda expressions.

Then for simplicity I moved all the #ifdefs inside call() which is a
wrapper to call_in_gui_thread(). So above, call() is essentially
call_in_gui_thread() as I just described.

The function passed to call() reads as follows:

1. Pass the eight arguments to doPrompt and return the result (so prompt
is essentially a wrapped for doPrompt. One could in fact now move the
whole contents of doPrompt inside the anonymous function, but I did not
do it.)

2. The eight arguments are stored as follows until the function is
destroyed (after the actual call): default_button, cancel_button, and
this are copied, and all the other variables are stored by reference.


+       auto compile = [=](std::string const & s) {
+               return clone->doExport(s, true);
+       };
+       return runAndDestroy(compile, orig, clone, format);

(I corrected the above because I think there was an error — which
unfortunately does not explain my initial problem.)

In the above example, the function "compile" calls clone->doExport where
"clone" is a copy of to the original pointer "clone", and where the
first argument is passed to it later by runAndDestroy.

The "auto" keyword asks to the compiler to deduce the type of the
function automatically, because the type of "compile" is too complicated
to write down. Note that it is still type-checked at compile-type (in
this case, the type is used to create an instance of the runAndDestroy
function template, and if compile was not an appropriate function, then
an error would occur at template instantiation. But one could also have
made the type explicit as std::function<Buffer::ExportStatus(std::string
const &)>).

You can also refer to https://stackoverflow.com/a/7627218 for other
examples and explanations.


Guillaume

Reply via email to