Trying & failing to make a function that returns functions... (C++11)

M

Mark

Hi,

I am trying to make a function that will create and return another
function (a closure that captures some state).

Here's the usage I want:

std::vector<std::string> on_off{"on", "off"};
StringValidatorFunction on_off_validator =
make_string_set_validator(on_off.begin(), on_off.end());
auto s = on_off_validator("on"); // s == "on"
auto t = on_off_validator("awf"); // throws
std::vector<std::string> color{"red", "green", "blue"};
StringValidatorFunction color_validator =
make_string_set_validator(color.begin(), color.end());
auto r = color_validator("red"); // s == "red"
auto y = color_validator("yellow"); // throws

Here's what I've got so far:

using StringValidatorFunction = std::function<std::string(const
std::string&)>;

template <class Iter>
StringValidatorFunction make_string_set_validator(const Iter first,
const Iter end)
{
std::unordered_set<std::string> valid_strings;
std::for_each(first.begin(), first.end(),
[&valid_strings](const std::string &s)
{valid_strings.insert(s);});
return [valid_strings](const std::string &s)->std::string{
if (valid_strings.find(s) == valid_strings.end())
throw ValueError("Invalid string '" + s + "'");
return s;
};
}

This is the first time I've tried to use lambdas (I'm just starting
out with C++11), and GCC 4.7.0 isn't helpful (at least not to me):

main.o: In function `main':
main.cpp:(.text+0x253): undefined reference to
`std::function<std::string ()(std::string const&)>
make_string_set_validator<__gnu_cxx::__normal_iterator<std::string*,
std::vector said:
(__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string,
std::allocator<std::string> > >,
__gnu_cxx::__normal_iterator<std::string*, std::vector<std::string,
std::allocator<std::string> > >)'
collect2: error: ld returned 1 exit status

Any suggestions welcome:)

Thanks!
 
M

Mark

Hi,

I am trying to make a function that will create and return another
function (a closure that captures some state).
[snip]

I'd made the silly mistake of putting the templated function in the
source rather than the header:-(

And realized that there's a slightly nicer usage possible:

std::vector<std::string> on_off{"on", "off"};
auto on_off_validator = make_string_set_validator(on_off);

Using:

template <class T>
StringValidatorFunction make_string_set_validator(const T
&valid_strings)
{
std::unordered_set<std::string> strings;
std::for_each(std::begin(valid_strings), std::end(valid_strings),
[&strings](const std::string &s){strings.insert(s);});
return [strings](const std::string &s)->std::string{
if (strings.find(s) == strings.end())
throw ValueError("Invalid string '" + s + "'");
return s;
};
}
 
J

Juha Nieminen

Mark said:
I'd made the silly mistake of putting the templated function in the
source rather than the header:-(

In principle there's nothing wrong with that, as long as you somehow
end up instantiating the template implementation will all the types with
which it's used in the program. Of course usually the easiest way to do
that is to put the implementation in the header where it's declared.

(It is, in fact, possible to simply declare (rather than define) a
template in a header, use only this declaration in the different
compilation units, and put the implementation of the template in one
single compilation unit where it's explicitly instantiated for each
used type. (C++ support a special syntax for explicit template
instantiation.) This is akin to the (now defunct) export template
mechanism, except that you have to do the instantiation manually, which
makes it infeasible in most practical cases.)
 
B

BGB

Hi,

I am trying to make a function that will create and return another
function (a closure that captures some state).
[snip]

I'd made the silly mistake of putting the templated function in the
source rather than the header:-(


<snip, example>

although I am not really experienced with C++11 nor do I have an easy
time following the code, it does look a little worrying:
as I understand it, returning (C++11) closures in this way may not be safe?

the issue is mostly where the space for any captured references is
stored, and if it is copied along with the closure, or is somewhere
"stable" (such as in the heap) vs somewhere unstable (say, on the stack).

non-capturing lambdas are known safe (and may also be cast to function
pointers);
lamdas which capturing references to locals are known unsafe (to return);
what about capturing values or references to data outside the parent frame?

(both safe and unsafe implementations can be easily imagined, but does
the standard mandate this case is safe? I don't actually know...).



ironically (OT / side-note):
my script VM doesn't have these issues (with closures), at the cost that
everything is heap allocated (they can also be coerced into C-style
function pointers as well). the tradeoff though is that they either have
to be deleted/freed, or risk triggering the GC to do its thing (say, if
closures are created in large numbers and/or at a rapid pace).

technically, they are "executable objects", allocated in RWX
(Read/Write/Execute) memory, with executable code (a "trampoline")
preceding any data members (so, one is essentially jumping into/through
a struct). sadly, they also have to be "locked"/"pinned" before they can
be safely used as callbacks (if stored in non-GC'ed memory), which
prevents them from being automatically GC'ed (allowing for a potentially
VM breaking edge case).


or such...
 
J

Juha Nieminen

BGB said:
what about capturing values or references to data outside the parent frame?

A lambda that captures by value is safe to outlive the scope of those
values. The captured objects are copied to a local stack of the lambda,
which lives for as long as the lambda itself. (How this is actually
implemented internally probably depends on the compiler.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top