views:

160

answers:

4

Consider the following class:

class Person {
public:
    // I don't want any "char *" to be converted to Person implicitly!
    explicit Person( const char * name ) : name_(name) {};

private:
    std::string name_;
};

Also consider following array of char* data:

char STUDENT_NAMES[][20] = {
    "Bart",
    "Liza",
    "Maggie"
};

Now, I want to create std::list of Person according to this array. All I could invent is to use std::transform algorithm with hand-written function object:

struct CreatePerson : public std::unary_function<const char*,Person> {
    Person operator() (const char * name) const {
     return Person(name);
    };
};

// ...

std::list<Person> students;
std::transform(
    &STUDENT_NAMES[ 0 ],
    &(STUDENT_NAMES[ sizeof(STUDENT_NAMES)/sizeof(STUDENT_NAMES[0]) ]),
    front_inserter(students),
    CreatePerson() );
// ...

Is there any shorter and/or clearer way to do it? Maybe some standard function objects or adaptors?

+5  A: 

You may use boost::lambda in the following way:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/construct.hpp>
#include <string>
#include <iterator>
#include <algorithm>
#include <list>

struct person {
  explicit person(char const *name)
   :name(name) { }
private:
  std::string name;
};

int main() {
  char names[][20] = {
    "Michael", "litb"
  };

  std::list<person> v;
  std::transform(names, names + 2, std::front_inserter(v), 
    boost::lambda::constructor<person>());
}

I don't think there is such a way with the Standard C++ Library.

Johannes Schaub - litb
I hope this array calcs are right now, finally. Ignore my dumb mentions what i had temporary in my answer about what yields what type. Srsly, array hax in the morning hurts my brain :) However, i still recommend you to make `STUDENT_NAMES` a `char const *names[] = { "Bart", "Lisa", ... };` - that way it can't overflow the 20 char limit anymore. I don't however know your code-base. Was there a strong reason for you to do it that way?
Johannes Schaub - litb
Your solution looks good, thank you!About array size. Thanks for your suggestion, but it is not the point of problem. My example is taken out of context and rewritten for ease of understanding. :)
Michael
@litb: _const_ char names[][20] results in no relocations for the data, and thus allows the data to live in read-only memory, such as EPROMs. OTHO, even _const_ char * names[] needs relocations (it's an array of pointers, and those pointers need to be relocated):http://people.redhat.com/drepper/dsohowto.pdf
#include <boost/range.hpp>std::transform( boost::begin(names), boost::end(names), ...:)
A: 

Not much help, but isn't there a standard ELEMENTS macro for

   sizeof(STUDENT_NAMES)/sizeof(STUDENT_NAMES[0])

Quick search didn't find it, but I feel like every implementation I have used has it.

Only other suggestion I have is to turn CreatePerson into a template -- that way, if you need it again, you just need to do CreateObj or something like that.

Lou Franco
A: 

Well I will just have written a method/function like :

convert(const char*[] names, std::list<Person>&);

Why not if just to do a simple conversion ? Maybe I miss the point ?

m2c

neuro
The point is in reusing iteration code defined in the standard library. The 'algorithm' header contains loads of useful functions for very common iterating tasks, not limited to lists, vectors, arrays, but using 'iterators'.
xtofl
@xtofl: Yes, I aggree with you. My point is that the added value in this case is not evident, because in the given example, you have to write an unary function outside of your Person class scope, and that just to apply a standard transform algorithm. Your solution (using constructor) is great and there the use of transform appear clean. But writing an unary just to use transform ...
neuro
+2  A: 

Your class' constructor is a conversion function. You don't need to transform: you can just insert. Take a look at this code here.

std::list<Person> ps( NAMES, NAMES+sizeof(NAMES)/sizeof(NAMES[0]) );

Otherwise, a shorter workaround would be using a static factory function:

struct Person {
    static Person create( const char* name );
     ....
 };

 std::transform( NAMES, NAMES+sizeof(NAMES)/sizeof(NAMES[0], front_inserter( ps ),
   &Person::create );
xtofl
+1 there the transform seems a lot more stylish than in the question example ...
neuro
No, it isn't. Please notice that my constructor is explicit.Second example with static create() really works fine, but it is only nice looking adoption of mine. You've just moved functionality from functor to static method. Thank you anyway.What I wanted to see is a method to directly create my object. For now it seems to me, there is no such ability in STL.
Michael
You are totally right. You asked for a nicer way - beit only slightly: you don't need the extra functor.
xtofl