tags:

views:

109

answers:

6
+2  Q: 

Table api

Suppose you have a table widget class.

Do you do table.row(i).column(0).setText(students[i].surname())
or table[0][i] = students[i].surname()

the latter makes way less sense but the simplicity is so luring ;)

ditto for: table.row(0).column(0).setBackground(red)
vs: table[0][0].setBackground(red)

Note that Table::row returns a Table::Row, whose column function returns a Table::Cell, and Table::Cell provides either setText or op= (as well as setBackground).
Same for Table::op[] and Table::Row::op[].

Your thoughts?

A: 

The second one. It carries just as much meaning as the first and is hugely easier to read and to type.(I don't really understand why you say it makes "way less sense".)

Tom Smith
It makes less sense because a table widget isn't a collection that can be unambiguously indexed. It is a GUI entity with several operations available on it, such as taking and manipulating a row from it. This is in contrast to, say, boost::multi_array, which _is_ just a collection so op[] is fine.
A: 

Actually you'd do neither of those. They are both buggy code and could lead to exceptions if there are no rows.

if(table.rows > 0)
{
    var row = table.row[0];
    if(row.columns > 0)
    {
        var col = row.column[0];
        etc...
Simon Hughes
Wrong: obviously the posted code is immediately preceded by table.resize(2, students.size()) or so. Don't nitpick foolproofness, the question is about which api makes sense.
A: 

The solution with Table::row() and Table::Row::column() methods is a bit more readable (in general) and allows you to unambiguously create a Table::column() method, Table::Column (proxy) class, and Table::Column::row() method later on, if that is ever needed. This solution also makes it easy to find all places where rows/columns are accessed, which is much harder when you use operator overloading.

As others pointed out however, the second solution is less typing and, in my opinion, not much worse in readability. (May even be more readable in certain situations.)

It's up to you to decide though, I'm just giving some implications of both solutions :-)

Tobi
grepping is not a concern here. Whether row()/column() are more readable than indexing is really arguable, I mean look at my example again. :)
I find that in your example the first solution is more readable :-)The major reason for that may be because for Table[0][0] in code I've never seen before I have to look up the Table class to see whether the first digit is the row or the column.
Tobi
Oh. I thought this was obvious because things are ordered x-y, where x means horizontal and y vertical.
A: 
table.row(i).column(0)

This style is known as the Named Parameter Idiom. This makes it easy to reorder a set of calls in any way that pleases you as an alternative to positional parameters. However, this works provided each of the chained calls return the same original object. Of course, in your case you have a row object and a column object. So, there isn't much to gain here.

The ease of use of the second construct provides another compelling reason to choose the latter over the former.

dirkgently
I like to view it as distinct from Named Parameter because unlike NP, it actually reflects my object model ;) i.e. just because I'm chaining calls doesn't mean I'm doing it as a crude workaround for C++'s ugliness. I'm mostly interested in the apparent conflict between a nice api and a correct api.
Btw, maybe my intention would've been clearer with "table.row(i).cell(0)" to emphasize that the cell is indeed part of the row and not directly of the table (contrary to NP).
+1  A: 

As a less verbose alternative for many common cases, I would also provide something like this:

table.rowcol(i, j) = "blah"; // rowcol returns directly a cell
table.colrow(k, t).SetBackground(black);

Basically the name of the method just serves as a reminder on the order of the parameters. Also, being a single method, you can perform better exception handling IMO.

UncleZeiv
That's a bit ugly but it's workable. It _can_ be viewed as less flexible than my solution, as you can't treat a row as an object (which, for one thing, means no std algos).
Please note that I am proposing this *in addition* to your API; I would keep the row() and column() methods for other specific cases.
UncleZeiv
A: 

There is not a way which is better than another. It depends on what you have to do with your data structure.

That said, for a simple table widget (assuming you are not coding a complex Excel-like application) I'd seek easy syntax instead of a more general interface. So table[0][1] = "blah" would just work fine for me.

happy_emi