Merge lp:~jpakkane/unity-scope-click/classness into lp:unity-scope-click

Proposed by Jussi Pakkanen
Status: Work in progress
Proposed branch: lp:~jpakkane/unity-scope-click/classness
Merge into: lp:unity-scope-click
Diff against target: 159 lines (+78/-30)
2 files modified
scope/click/index.cpp (+58/-26)
scope/click/index.h (+20/-4)
To merge this branch: bzr merge lp:~jpakkane/unity-scope-click/classness
Reviewer Review Type Date Requested Status
Thomas Voß (community) Disapprove
PS Jenkins bot continuous-integration Approve
Ubuntu One hackers Pending
Review via email: mp+205768@code.launchpad.net

Commit message

Made structs more C++-like.

Description of the change

Made a few structs more C++-like. This includes moving comparison operators to class methods. This is the commonly accepted best practice. Also made PackageDetails JSON parsing work via a constructor.

To post a comment you must log in.
134. By Jussi Pakkanen

Removed redundant thises.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Thomas Voß (thomas-voss) wrote :

I would argue against "classifying" the struct as it really is a value type, according to Meyers: Effective C++ Item 23 "Prefer non-member non-friend functions to member functions", symmetric operators should be non-member, non-friend functions (see http://stackoverflow.com/questions/4421706/operator-overloading/4421729#4421729 for a good discussion).

With that, I disapprove of the changes.

review: Disapprove
Revision history for this message
Jussi Pakkanen (jpakkane) wrote :

"Classify" was perhaps a poorly chosen name but I could not think of anything better. That being said:

This MR does _not_ change the semantics of the target in any way, it remains a struct with the exact same format. The only API change is that in addition to having a constructor that takes all elements you also get a constructor that takes a file name pointing to a json file. This follows the principle that it is better to initialize everything in the constructor rather than creating empty objects and then calling some type of an initializer function.

As far as member operators go, the main point in that style recommendation is that it increases encapsulation. In this particular case that point is moot, since the comparison function accesses struct members directly. There is no encapsulation at all.

The main argument for implementing comparison operators as member functions is information centralisation. This allows you to look at just the struct definition and immediately know what operations it supports. A plain function comparison operator is easy to overlook and can thus cause problems when new person starts looking through the code and can even be accidentally lost when refactoring.

Revision history for this message
Thomas Voß (thomas-voss) wrote :

> "Classify" was perhaps a poorly chosen name but I could not think of anything
> better. That being said:
>
> This MR does _not_ change the semantics of the target in any way, it remains a
> struct with the exact same format. The only API change is that in addition to
> having a constructor that takes all elements you also get a constructor that
> takes a file name pointing to a json file. This follows the principle that it
> is better to initialize everything in the constructor rather than creating
> empty objects and then calling some type of an initializer function.
>

Sure, two stage init is bad, but why not have a free function from_json? With that, you wouldn't need the c'tor of epic size.

> As far as member operators go, the main point in that style recommendation is
> that it increases encapsulation. In this particular case that point is moot,
> since the comparison function accesses struct members directly. There is no
> encapsulation at all.
>
> The main argument for implementing comparison operators as member functions is
> information centralisation. This allows you to look at just the struct
> definition and immediately know what operations it supports. A plain function
> comparison operator is easy to overlook and can thus cause problems when new
> person starts looking through the code and can even be accidentally lost when
> refactoring.

Hmmm, the compiler & tests capture if someone forgets about an operator== during refactorings, not sure how having it defined as a member of the struct really helps.

Unmerged revisions

134. By Jussi Pakkanen

Removed redundant thises.

133. By Jussi Pakkanen

Made Package and PackageDetails more classlike.

132. By Jussi Pakkanen

Moved comparison operators to member functions as is the recommended way.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'scope/click/index.cpp'
2--- scope/click/index.cpp 2014-02-06 21:32:20 +0000
3+++ scope/click/index.cpp 2014-02-11 13:41:35 +0000
4@@ -36,30 +36,30 @@
5 namespace click
6 {
7
8-bool operator==(const Package& lhs, const Package& rhs) {
9- return lhs.name == rhs.name &&
10- lhs.title == rhs.title &&
11- lhs.price == rhs.price &&
12- lhs.icon_url == rhs.icon_url &&
13- lhs.url == rhs.url;
14+bool Package::operator==(const Package& rhs) const {
15+ return name == rhs.name &&
16+ title == rhs.title &&
17+ price == rhs.price &&
18+ icon_url == rhs.icon_url &&
19+ url == rhs.url;
20 }
21
22-bool operator==(const PackageDetails& lhs, const PackageDetails& rhs) {
23- return lhs.name == rhs.name &&
24- lhs.title == rhs.title &&
25- lhs.icon_url == rhs.icon_url &&
26- lhs.description == rhs.description &&
27- lhs.download_url == rhs.download_url &&
28- lhs.rating == rhs.rating &&
29- lhs.keywords == rhs.keywords &&
30- lhs.terms_of_service == rhs.terms_of_service &&
31- lhs.license == rhs.license &&
32- lhs.publisher == rhs.publisher &&
33- lhs.main_screenshot_url == rhs.main_screenshot_url &&
34- lhs.more_screenshots_urls == rhs.more_screenshots_urls &&
35- lhs.binary_filesize == rhs.binary_filesize &&
36- lhs.version == rhs.version &&
37- lhs.framework == rhs.framework;
38+bool PackageDetails::operator==(const PackageDetails& rhs) const {
39+ return name == rhs.name &&
40+ title == rhs.title &&
41+ icon_url == rhs.icon_url &&
42+ description == rhs.description &&
43+ download_url == rhs.download_url &&
44+ rating == rhs.rating &&
45+ keywords == rhs.keywords &&
46+ terms_of_service == rhs.terms_of_service &&
47+ license == rhs.license &&
48+ publisher == rhs.publisher &&
49+ main_screenshot_url == rhs.main_screenshot_url &&
50+ more_screenshots_urls == rhs.more_screenshots_urls &&
51+ binary_filesize == rhs.binary_filesize &&
52+ version == rhs.version &&
53+ framework == rhs.framework;
54 }
55
56 PackageList package_list_from_json(const std::string& json)
57@@ -86,7 +86,41 @@
58 return pl;
59 }
60
61-void PackageDetails::loadJson(const std::string &json)
62+
63+PackageDetails::PackageDetails(const std::string &name,
64+ const std::string &title,
65+ const std::string &icon_url,
66+ const std::string &description,
67+ const std::string &download_url,
68+ const std::string &rating,
69+ const std::string &keywords,
70+ const std::string &terms_of_service,
71+ const std::string &license,
72+ const std::string &publisher,
73+ const std::string &main_screenshot_url,
74+ const std::string &more_screenshots_urls,
75+ const std::string &binary_filesize,
76+ const std::string &version,
77+ const std::string &framework) :
78+ name(name),
79+ title(title),
80+ icon_url(icon_url),
81+ description(description),
82+ download_url(download_url),
83+ rating(rating),
84+ keywords(keywords),
85+ terms_of_service(terms_of_service),
86+ license(license),
87+ publisher(publisher),
88+ main_screenshot_url(main_screenshot_url),
89+ more_screenshots_urls(more_screenshots_urls),
90+ binary_filesize(binary_filesize),
91+ version(version),
92+ framework(framework)
93+{
94+}
95+
96+PackageDetails::PackageDetails(const std::string &json)
97 {
98 std::istringstream is(json);
99 boost::property_tree::ptree node;
100@@ -130,9 +164,7 @@
101 QSharedPointer<click::web::Response> response = service->call(click::DETAILS_PATH+package_name);
102 QObject::connect(response.data(), &click::web::Response::finished, [=](QString reply){
103 Q_UNUSED(response); // so it's still in scope
104- click::PackageDetails d;
105- d.loadJson(reply.toUtf8().constData());
106- callback(d);
107+ callback(click::PackageDetails(reply.toUtf8().constData()));
108 });
109 }
110
111
112=== modified file 'scope/click/index.h'
113--- scope/click/index.h 2014-02-06 21:22:48 +0000
114+++ scope/click/index.h 2014-02-11 13:41:35 +0000
115@@ -54,6 +54,7 @@
116 std::string icon_url;
117 std::string url;
118 void matches (std::string query, std::function<bool> callback);
119+ bool operator==(const Package& rhs) const;
120 };
121
122 typedef std::list<Package> PackageList;
123@@ -77,7 +78,25 @@
124 std::string binary_filesize;
125 std::string version;
126 std::string framework;
127- void loadJson(const std::string &json);
128+
129+ PackageDetails(const std::string &name,
130+ const std::string &title,
131+ const std::string &icon_url,
132+ const std::string &description,
133+ const std::string &download_url,
134+ const std::string &rating,
135+ const std::string &keywords,
136+ const std::string &terms_of_service,
137+ const std::string &license,
138+ const std::string &publisher,
139+ const std::string &main_screenshot_url,
140+ const std::string &more_screenshots_urls,
141+ const std::string &binary_filesize,
142+ const std::string &version,
143+ const std::string &framework);
144+ explicit PackageDetails(const std::string &json);
145+
146+ bool operator==(const PackageDetails& rhs) const;
147 };
148
149 class Index
150@@ -91,9 +110,6 @@
151 ~Index();
152 };
153
154-bool operator==(const Package& lhs, const Package& rhs);
155-bool operator==(const PackageDetails& lhs, const PackageDetails& rhs);
156-
157 } // namespace click
158
159 #endif // CLICKINDEX_H

Subscribers

People subscribed via source and target branches

to all changes: