0

Program 1:

#include <iostream>

std::string Hello(void){return "Hello";}
std::string World(void){return "world!";}

int main(){

    std::cout << Hello() << " " << World() << std::endl;
}

The functions Hello() and World() exist in the global namespace.
This means they can be called by any other function that comes after their declaration (or in this case, I provided the definition and did not need the declaration first).

I have a problem with this, because as my project gets bigger, I include more header files that fill the global namespace with lots of functions, and I risk having function signature collisions, or worse, accidentally calling the wrong function that should only be called as a sub-task of another function.

I am trying to follow the paradaim of functionally decomposing a task into sub-tasks, and thus it would not make sense for particular functions to ever be called outside the scope of another particular function.

Here is a work-around, and apart from the code becoming unreadable due to the indentation depth, I want to know if there are any performance or implementation gotchas. lambda functions are a bit of magic for me at the moment, so I'm curious about the unforeseen dangers.

Program 2:

#include <iostream>

int main(){

    auto Hello = [](void) -> std::string{return "Hello";};
    auto World = [](void) -> std::string{return "world!";};

    std::cout << Hello() << " " << World() << std::endl;
}

Hello() and World() are encapsulated inside main() and can not be called from outside main()'s scope.
Is that all that's different?

Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • 6
    What's wrong with namespaces? –  Dec 20 '12 at 00:00
  • 2
    'Use namespaces' is too simple an answer because the same namespace can be used across several modules. A good example simply being the global, well, namespace. – Luc Danton Dec 20 '12 at 00:47

3 Answers3

1

The preferred mechanic in C++ to achieve what you want is to make things (functions, types, objects) private (not in the sense of accessibility, unlike the keyword) to a module, such that they cannot be named in another. This prevents collisions altogether.

For instance, consider a module consisting of a header file:

// List dependencies
#include <string>

// It's customary to put things in a namespace
// Note that namespaces are commonly cross-module

namespace ns {

// Declarations visible to all
std::string hello_world();

} // ns

and such an implementation:

// include header listed above
#include <header.hpp>

// This contains declarations visible to this module only
namespace {

std::string hello_part();

// This is both a declaration and a definition
std::string world_part()
{ return "world!"; }

} // namespace

// Definition of function declared in header
std::string ns::hello_world()
{
    // Implemention defers to private parts that are visible here
    return hello_part() + " " + world_part();
}

// Definition of private function
// We need to reopen the unnamed namespace for that

namespace {

std::string hello_part()
{ return "Hello"; }

} // namespace

Now another module can declare another hello_part on their own, which needn't be a function with the same declaration or even be a function at all, and there won't be a conflict. There will be two different and separate entities -- much like a for statement can have their own int i; variable with interfering without any other i!

As for the other part of your question, it doesn't matter much if the hello_part and world_part above are implemented as plain old functions or as function objects (via lambda expressions or otherwise). But keep in mind that since they don't close over anything, they're not much of a closure. C++ lambda expressions do allow for closing over anything, including local variables (also known as local captures), but the type system is not quite expressive enough to allow for the upwards funarg problem without paying a small penalty in certain situations. True to the C++ creed though, this can't happen behind your back, and you will need to spell out that penalty via e.g. std::function. For those cases I would argue against lambda expressions as I think there are better alternatives (on which I won't expand here).

In general though, deciding whether you want to use a lambda expression is a consideration that will not (and should not) affect modularity of your program.

A longer and more detailed answer would explain the different roles of scopes and visibility, declarations and implementations, namespaces, modules, and accessibility which can all be used to make a program modular. It is best not to conflate them. My example uses all of those things except for accessibility to achieve its needs, but it's far from the only way to do it.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • Good writeup, but it doesn't really answer the question – "*I want to know if there are any performance or implementation gotchas.*" It seems to me that the answer would be 'no', even if other designs are more practical. – ildjarn Dec 20 '12 at 01:16
  • @ildjarn Second to last paragraph, but I need to touch it up. – Luc Danton Dec 20 '12 at 01:20
0

Firstly, it's called "namespaces". Secondly, there aren't really any downsides to that kind of lambda. In fact, they have upsides, because you can now use local variables inside them, which permits re-using more logic.

Puppy
  • 144,682
  • 38
  • 256
  • 465
0

I would not do it. In your case what you will end up is creating huge functions as to allow the definition of the lambdas inside, and you will end up with functions where the contents of the body are much harder to maintain.

There have been very large projects before C++11 and lambdas, and the risk of collisions has been managed by different means, including namespaces as DeadMG already mentioned, but also classes and object oriented design and other forms of encapsulation (define local functions as static at namespace level in the implementation file, forcing internal linkage and avoiding conflicts with other translation units. Above that, if you carefully choose meaningful names for your identifiers, you should avoid 99% of the collisions.

If you really are going to work on a large scale project consider reading John's Lakos Large Scale C++ Software Design

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489