tags:

views:

120

answers:

5

Example routine:

const Armature* SceneFile::findArmature(const Str& name){
    for (int i = 0; i < (int)armatures.size(); i++)
        if (name == armatures[i].name)
            return &armatures[i];
    return 0;
}

Routine's purpose is (obviously) to find a value within an array of elements, based on element's member variable, where comparing member variable with external "key" is search criteria.

One way to do it is to iterate through array in loop. Another is to use some kind of "map" class (std::map, some kind of vector sorted values + binarySearch, etc, etc). It is also possible to make a class for std::find or for std::for_each and use it to "wrap" the iteration loop.

What are other ways to do that?

I'm looking for alternative ways/techniques to extract the required element. Ideally - I'm looking for a language construct, or a template "combo", or a programming pattern I don't know of that would collapse entire loop or entire function into one statement. Preferably using standard C++/STL features (no C++0x, until it becomes a new standard) AND without having to write additional helper classes (i.e. if helper classes exist, they should be generated from existing templates).

I.e. something like std::find where comparison is based on class member variable, and a variable is extracted using standard template function, or if variable (the one compared against "key"("name")) in example can be selected as parameter.

The purpose of the question is to discover/find language feature/programming technique I don't know yet. I suspect that there may be an applicable construct/tempalte/function/technique similar to for_each, and knowing this technique may be useful. Which is the main reason for asking.

Ideas?

+1  A: 

Sure looks like a case for std::find_if -- as the predicate, you could use e.g. a suitable bind1st. I'm reluctant to say more as this smacks of homework a lot...;-).

Alex Martelli
Nice one. The only problem is that I do not see a way to extract "name" for comparison in bind1st without writing another routine or class.
SigTerm
If `.name()` was an accessor function (the normal idiom, after all!), `std::mem_fun` would help, of course (and such helpers are one more reason why accessor functions are a good idea;-). If you're stuck with a data member, consider Boost::bind -- see http://www.boost.org/doc/libs/1_43_0/libs/bind/bind.html#with_member_pointers ; you can hardly expect much support for justly unpopular idioms in the standard library, after all, but Boost is much vaster and its `bind` library does support binding via data-member pointers-to-members.
Alex Martelli
A: 

You would probably need to use STL map. It gives you possibility to get the element using keys. Your key would be the name of armature.

http://www.cplusplus.com/reference/stl/map/

EDIT: :D

one liner B-)

const Armature* SceneFile::findArmature(const Str& name){for (int i = 0; i < (int)armatures.size(); i++) if(name == armatures[i].name) return &armatures[i]; return 0;}
Adi
several problems: it still will take 5 lines, might be even longer because of things like std::map<>::iterator. Keys will be case-sensitive, or I'll have to write another "Compare" class which will add even more lines. Also it will store redundant data - armature already has "name" field. If I keep "name" in armature, std::map will store another copy for no reason. If I move name from armature into map key - I won't be able to extract name from armature pointer/reference, unless I have access to a class with std::map in it.
SigTerm
@Sig: Why are you so concerned with line count?
GMan
@GMan: Already answered that in comment to your post.
SigTerm
A: 

Holy shiz, you're using _stricmp? FAIL. Also, you didn't actually tell us the type of the vectors or any of the variables involved, so this is just guesswork.

const Armature* SceneFile::findArmature(const std::string& lols) {
    for(auto it = armatures.begin(); it != armatures.end(); it++) {
        if (boost::iequals(lols, (*it).name))
            return &(*it);
    return NULL;
}

Ultimately, if you need this, you should put the armatures or pointers to them in a std::map. A vector is the wrong container if you're searching into it, they're best for when the collection is what's important rather than any finding behaviour.

Edited to use a std::string reference.

DeadMG
-1 for "FAIL", +1 for lambda, -1 for not mentioning it's not a solution for the current C++ standard, +1 for suggesting a map.
GMan
@Gman: Comparing a pair of const char* when you can construct a string and compare is a fail.
DeadMG
@Dead: I just meant your presentation. :) Telling someone they fail just shuts them down and puts them on defense, and they'll never want to listen to you.
GMan
@GMan: I'm not going to sugar-coat it. If the OP wrote that class, then he needs to attend basic remedial C++. We're not talking about the subtleties of SFINAE here, it's simple object orientation and using the right container for the right job.
DeadMG
7 lines. If you dislike stricmp, give example of short C++ case-insensitive string comparison, or don't waste time. The type of object can be described as class containing several vectors of other classes containing several vectors of other classes (repeat).... It is a tree of different objects that takes several megabytes of RAM in a most compact representation. Zero pointers and "not found" situations are already handled with nice custom crash(const char* message, ...) and stateCheck(bool state, const char* msg, ...) routines. Also, your comparison is case-sensitive.
SigTerm
@SigTerm: What IS it with you and the number of lines? I'm beginning to think that you flat out can't code in any language, let alone the half-C half-C++ you have going on up there.
DeadMG
@DeadMG: Dude. My question is very simple : write alternative implementation using 5 lines of code or less without messing up code formatting. If you can do it, write an example. If you can't - I have nothing to talk about with you. Stick to the problem. I didn't ask your opinion about stricmp, you don't know the purpose of the code, your implementation behaves differently. Judging by your replies you are several years younger than me and have several years less of experience. Unless you have a solution (or if you can tell me something I don't already know), I'm not interested in arguing.
SigTerm
+1  A: 

Why 5 lines? Clean doesn't have a number attached to it. In fact, clean code might take more lines in the utility classes, which can then be reused over and over. Don't restrict yourself unnecessarily.

class by_name
{
public:
    by_name(const std::string& pName) :
    mName(pName)
    {}

    template <typename T>
    bool operator()(const T& pX)
    {
        return pX.name == pName;
    }

private:
    std::string mName;
};

Then:

const Armature* SceneFile::findArmature(const char* name)
{
    // whatever the iterator type name is
    auto iter = std::find_if(armatures.begin(), armatures.end(), by_name(name));
    return iter == armatures.end() ? 0 : &(*iter);
}

Within restriction:

class by_name { public: by_name(const std::string& pName) : mName(pName) {} template <typename T> bool operator()(const T& pX) { return pX.name == pName; } private: std::string mName; };

Then:

const Armature* SceneFile::findArmature(const char* name)
{
    // whatever the iterator type name is
    auto iter = std::find_if(armatures.begin(), armatures.end(), by_name(name));
    return iter == armatures.end() ? 0 : &(*iter);
}

:)


C++0x has ranged-based for-loops, which I think would make the most elegant solution:

const Armature* SceneFile::findArmature(const std::string& pName) const
{
    for (auto a : armatures)
    {
        if (a.name = pName) return &a;
    }

    return 0;
}
GMan
"Why 5 lines?" Because I already know how to do it with more than 5 lines. But I don't know how to do it differently with 5 or less lines. Which is why I asked - there may be a different way (I'm not aware of) to do this.
SigTerm
@Sig: Lines are so arbitrary, though. I would argue my code is more elegant, and it takes more lines. (C++0x will help with elegance, I'll add that.)
GMan
@GMan: I meant to say, that there should be concepts or techniques In C++ I missed or don't know yet for some reason. The "for" loop smells like it should take one line *at most*. By adding 5 lines restriction, I make question non-trivial (messing up formatting isn't the solution, by the way), which means that to solve the problem a person will have to apply technique I'm not aware of. You can think that it is a brain exercise.
SigTerm
@Sig: Until you can get away from the terrible "less lines is better" idea, you'll never write actual elegant code. Elegance has nothing to do with line count, it has to do with how easy the code reads and how simple the design is. Line count has nothing to do with either of those.
GMan
@GMan: Perhaps I don't know english well enough to explain question properly - because I already tried to explain it to you and it obviously didn't work out. I don't think that "less lines == more elegance" - you're jumping to conclusions here. I was looking for new language construct or a template construct I don't know of.
SigTerm
@Sig: Your questions and responses are very misleading, then. Your English is quite well, by the way. Perhaps the question should simply be "What's the best way to do: ____, here's how I did it." Then you can simply take in replies as they come. Lines of code is seriously unrelated, though. Best merely correlates to lines of code, it's not caused by it.
GMan
@GMan: I'll think about it. Maybe I'll be able able to rephrase things a bit.
SigTerm
@GMan: I've rewritten a question, you can give it another try, if you wish. I don't think I can make it clearer than that.
SigTerm
@Sig: I've taken off my -1, but I still feel with "collapse entire loop or entire function into *one statement*" you're still somehow convinced less is more, which it isn't. :/
GMan
+1  A: 

If you have access to Boost or another tr1 implementation, you can use bind to do this:

const Armature * SceneFile::findArmature(const char * name) {
  find_if(armatures.begin(), armatures.end(),
    bind(_stricmp, name, bind(&string::c_str, bind(&Armature::name, _1))) == 0);
}

Caveat: I suspect many would admit that this is shorter, but claim it fails on the more elegant/simpler criteria.

SCFrench
Not exactly what I've been looking for, but it is pretty close to what I expected to find.
SigTerm