How to Write elegant code (1) -- flexible use of Goto and _ Try

Source: Internet
Author: User

// ================================================ ========================================
// Title:
// How to Write elegant code (1) -- flexible use of Goto and _ Try
// Author:
// Norains
// Date:
// Thursday 16-July-2009
// Environment:
// Wince5.0 + vs2005
// ================================================ ========================================
Is goto a poison? Where you can use Goto, you can certainly achieve the same purpose in a structured manner! It is estimated that many of my friends will not be unfamiliar with this argument, or even be familiar with it! But it does not mean elegance. Believe it? Let's take a look.

Assume that we have a function that needs to implement the following functions: read some content of a driver to the cache; because the cache is globally public, therefore, we naturally use mutex for control. First of all, we insist on implementing it in a structured way. It is likely that our code is similar to the following:
View plaincopy to clipboardprint?
1. bool readdevicebuf ()
2 .{
3. entercriticalsection (& g_csbuf );
4.
5. // open the driver
6. Handle hdev = createfile (text ("dev1 :"),
7. file_write_attributes,
8. 0,
9. null,
10. open_existing,
11. file_attribute_normal | file_flag_write_through,
12. null );
13.
14. If (hdev = invalid_handle_value)
15 .{
16. leavecriticalsection (& g_csbuf );
17. Return false;
18 .}
19.
20. // obtain the cache size of the driver
21. DWORD dwsize = 0;
22. If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false)
23 .{
24. closehandle (hdev );
25. leavecriticalsection (& g_csbuf );
26. Return false;
27 .}
28.
29. // allocate Cache
30. g_pbuf = new byte [dwsize];
31. If (g_pbuf = NULL)
32 .{
33. closehandle (hdev );
34. leavecriticalsection (& g_csbuf );
35. Return false;
36 .}
37.
38. // read data from the driver
39. If (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false)
40 .{
41. Delete [] g_pbuf;
42. g_pbuf = false;
43.
44. closehandle (hdev );
45. leavecriticalsection (& g_csbuf );
46. Return false;
47 .}
48.
49.
50. closehandle (hdev );
51. leavecriticalsection (& g_csbuf );
52.
53. Return true;
54 .}
Bool readdevicebuf () {entercriticalsection (& g_csbuf); // open the driver device handle hdev = createfile (text ("dev1:"), file_write_attributes, 0, null, open_existing, file_attribute_normal | file_flag_write_through, null); If (hdev = invalid_handle_value) {values (& g_csbuf); Return false;} // obtain the cache size of the driver DWORD dwsize = 0; if (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false) {closehandle (hdev); leavecriticalsection (& g_csbuf ); return false;} // allocate cache g_pbuf = new byte [dwsize]; If (g_pbuf = NULL) {closehandle (hdev); leavecriticalsection (& g_csbuf); Return false ;} // read data from the driver if (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false) {Delete [] g_pbuf; g_pbuf = false; closehandle (hdev); leavecriticalsection (& g_csbuf); Return false;} closehandle (hdev); leavecriticalsection (& g_csbuf); Return true ;}
Yes, this structured method does solve the problem. But are we a little awkward? Each time an error occurs, the resource must be cleared before false is returned. Small functions may not be a big problem. As long as you open your eyes and be careful, you can still correctly handle resource release in subsequent responses. However, if a function is to add more and more functions and someone else may want to maintain this code, can it ensure that resources are released before return?

Next, we will use the Goto, which we despise, to see what will happen:
View plaincopy to clipboardprint?
1. bool readdevicebuf ()
2 .{
3. bool Bres = false;
4.
5. entercriticalsection (& g_csbuf );
6.
7. DWORD dwsize = 0;
8.
9. // open the driver
10. Handle hdev = createfile (text ("dev1 :"),
11. file_write_attributes,
12. 0,
13. null,
14. open_existing,
15. file_attribute_normal | file_flag_write_through,
16. null );
17.
18. If (hdev = invalid_handle_value)
19 .{
20. Goto exit;
21 .}
22.
23. // get the cache size of the driver
24. // DWORD dwsize = 0; // compilation error: initialization of 'dwsize' is skipped by 'Goto eg'
25. If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false)
26 .{
27. Goto exit;
28 .}
29.
30. // allocate Cache
31. g_pbuf = new byte [dwsize];
32. If (g_pbuf = NULL)
33 .{
34. Goto exit;
35 .}
36.
37. // read data from the driver
38. If (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false)
39 .{
40. Goto exit;
41 .}
42.
43. Bres = true;
44.
45. Exit:
46.
47. If (Bres = false)
48 .{
49. Delete [] g_pbuf;
50. g_pbuf = false;
51 .}
52.
53. closehandle (hdev );
54. leavecriticalsection (& g_csbuf );
55.
56. Return Bres;
57 .}
Bool readdevicebuf () {bool Bres = false; entercriticalsection (& g_csbuf); DWORD dwsize = 0; // open the driver device handle hdev = createfile (text ("dev1 :"), file_write_attributes, 0, null, open_existing, file_attribute_normal | file_flag_write_through, null); If (hdev = invalid_handle_value) {goto exit ;} // obtain the cache size of the driver device // DWORD dwsize = 0; // a compilation error occurs: initialization of 'dwsize' is skipped by 'Goto exit 'If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false) {goto exit;} // allocate cache g_pbuf = new byte [dwsize]; if (g_pbuf = NULL) {goto exit;} // read data from the driver if (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false) {goto exit;} Bres = true; Exit: If (Bres = false) {Delete [] g_pbuf; g_pbuf = false;} closehandle (hdev ); leavecriticalsection (& g_csbuf); Return Bres ;}
How is it? Release all resources in the exit section. Each entercriticalsection can correspond to a leavecriticalsection. Is it more elegant than before? Can you say that GOTO is a chicken rib?

However, Goto is not perfect. For example, the variable dwsize cannot be initialized after Goto, and the local variable can only be initialized before the first Goto. According to C ++'s suggestion, it is best to make the declaration of variables as close as possible to use. But before the first Goto, It's Just C style!

In fact, it is feasible to directly declare dwsize without initialization. However, this does not mean that it can be unobstructed in other circumstances. Maybe some programs depend on the initialization value, who knows?

Are there any more elegant ones? Can this solve the location problem of dwsize? Naturally, the answer is yes. However, we must invite our _ try to play:
View plaincopy to clipboardprint?
1. bool readdevicebuf ()
2 .{
3. bool Bres = false;
4.
5. entercriticalsection (& g_csbuf );
6.
7. // open the driver
8. Handle hdev = createfile (text ("dev1 :"),
9. file_write_attributes,
10. 0,
11. null,
12. open_existing,
13. file_attribute_normal | file_flag_write_through,
14. null );
15.
16. _ Try
17 .{
18. If (hdev = invalid_handle_value)
19 .{
20. _ leave;
21 .}
22.
23. // get the cache size of the driver
24. DWORD dwsize = 0;
25. If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false)
26 .{
27. _ leave;
28 .}
29.
30. // allocate Cache
31. g_pbuf = new byte [dwsize];
32. If (g_pbuf = NULL)
33 .{
34. _ leave;
35 .}
36.
37. // read data from the driver
38. If (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false)
39 .{
40. _ leave;
41 .}
42.
43. Bres = true;
44.
45 .}
46. _ finally
47 .{
48.
49. If (Bres = false)
50 .{
51. Delete [] g_pbuf;
52. g_pbuf = false;
53 .}
54.
55. closehandle (hdev );
56. leavecriticalsection (& g_csbuf );
57 .}
58.
59. Return Bres;
60 .}
Bool readdevicebuf () {bool Bres = false; entercriticalsection (& g_csbuf); // open the driver device handle hdev = createfile (text ("dev1:"), file_write_attributes, 0, null, open_existing, file_attribute_normal | file_flag_write_through, null); _ Try {If (hdev = invalid_handle_value) {_ leave;} // obtain the cache size of the driver DWORD dwsize = 0; if (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false) {_ leave ;} // allocate cache g_pbuf = new byte [dwsize]; If (g_pbuf = NULL) {_ leave;} // read data from the driver if (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_pbuf, dwsize, null, null) = false) {_ leave;} Bres = true;} _ finally {If (Bres = false) {Delete [] g_pbuf; g_pbuf = false;} closehandle (hdev); leavecriticalsection (& g_csbuf);} return Bres ;}
Oh yeah! Now dwsize is at last in the position where it should appear, isn't it more elegant than goto?

This code is rewritten using the seh mechanism. If the seh mechanism needs to be explained in detail, it is not a single word, so I will skip it here. Interested friends can find information on the Internet by themselves. Here, it just shows that the seh mechanism is used. In any case, the _ finally code segment must be run, unless there is an interruption in the middle.

Does the last paragraph mean that any place where goto can be used can be replaced by _ Try? The answer is no. Especially when STL is used in the Code, the seh mechanism will be powerless.

Believe it? Let's add a code segment to see the truth. Suppose we keep the cache not through arrays, but in the STL vector. After successfully reading the data, we want to output each value. The code can be as follows:
View plaincopy to clipboardprint?
1. bool readdevicebuf ()
2 .{
3. bool Bres = false;
4.
5. entercriticalsection (& g_csbuf );
6.
7. // open the driver
8. Handle hdev = createfile (text ("dev1 :"),
9. file_write_attributes,
10. 0,
11. null,
12. open_existing,
13. file_attribute_normal | file_flag_write_through,
14. null );
15.
16. _ Try
17 .{
18. If (hdev = invalid_handle_value)
19 .{
20. _ leave;
21 .}
22.
23. // get the cache size of the driver
24. DWORD dwsize = 0;
25. If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false)
26 .{
27. _ leave;
28 .}
29.
30. // allocate Cache
31. g_vtbuf.resize (dwsize );
32.
33. // read data from the driver
34. If (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_vtbuf [0], g_vtbuf.size (), null, null) = false)
35 .{
36. _ leave;
37 .}
38.
39.
40. // print each data
41. // The Code cannot be compiled here. The system prompts "error c2712: cannot use _ Try in functions that require object unwinding"
42. For (STD: vector: iterator iter = g_vtbuf.begin (); iter! = G_vtbuf.end (); ITER ++)
43 .{
44. printf ("% d/N", * ITER );
45 .}
46.
47. Bres = true;
48.
49 .}
50. _ finally
51 .{
52.
53. If (Bres = false)
54 .{
55. g_vtbuf.clear ();
56 .}
57.
58. closehandle (hdev );
59. leavecriticalsection (& g_csbuf );
60 .}
61.
62. Return Bres;
63 .}
Bool readdevicebuf () {bool Bres = false; entercriticalsection (& g_csbuf); // open the driver device handle hdev = createfile (text ("dev1:"), file_write_attributes, 0, null, open_existing, file_attribute_normal | file_flag_write_through, null); _ Try {If (hdev = invalid_handle_value) {_ leave;} // obtain the cache size of the driver DWORD dwsize = 0; if (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = Fals E) {_ leave;} // allocate the cache g_vtbuf.resize (dwsize); // read data from the driver if (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_vtbuf [0], g_vtbuf.size (), null, null) = false) {_ leave;} // print each data. // compilation fails here, the message "error c2712: cannot use _ Try in functions that require object unwinding" for (STD: vector: iterator iter = g_vtbuf.begin (); iter! = G_vtbuf.end (); ITER ++) {printf ("% d/N", * ITER);} Bres = true;} _ finally {If (Bres = false) {g_vtbuf.clear ();} closehandle (hdev); leavecriticalsection (& g_csbuf);} return Bres ;}

Unfortunately, this Code cannot be compiled. The object is used in the STL iterator, and the object releases the C ++ exception, which conflicts with Seh. Of course, we can use new instead to avoid this problem, but in this way, it makes the code more concise, and the elegance is even less than a pole.

At this time, you can only use Goto:
View plaincopy to clipboardprint?
1. bool readdevicebuf ()
2 .{
3. bool Bres = false;
4.
5. entercriticalsection (& g_csbuf );
6.
7. // open the driver
8. Handle hdev = createfile (text ("dev1 :"),
9. file_write_attributes,
10. 0,
11. null,
12. open_existing,
13. file_attribute_normal | file_flag_write_through,
14. null );
15.
16.
17. If (hdev = invalid_handle_value)
18 .{
19. Goto exit;
20 .}
21.
22. // get the cache size of the driver
23. DWORD dwsize = 0;
24. If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false)
25 .{
26. Goto exit;
27 .}
28.
29. // allocate Cache
30. g_vtbuf.resize (dwsize );
31.
32. // read data from the driver
33. If (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_vtbuf [0], g_vtbuf.size (), null, null) = false)
34 .{
35. Goto exit;
36 .}
37.
38.
39. // print each data
40. // The Code cannot be compiled here. The system prompts "error c2712: cannot use _ Try in functions that require object unwinding"
41. For (STD: vector: iterator iter = g_vtbuf.begin (); iter! = G_vtbuf.end (); ITER ++)
42 .{
43. printf ("% d/N", * ITER );
44 .}
45.
46. Bres = true;
47.
48. Exit:
49.
50.
51. If (Bres = false)
52 .{
53. g_vtbuf.clear ();
54 .}
55.
56. closehandle (hdev );
57. leavecriticalsection (& g_csbuf );
58.
59.
60. Return Bres;
61 .}
Bool readdevicebuf () {bool Bres = false; entercriticalsection (& g_csbuf); // open the driver device handle hdev = createfile (text ("dev1:"), file_write_attributes, 0, null, open_existing, file_attribute_normal | file_flag_write_through, null); If (hdev = success) {goto exit;} // get the cache size of the driver device DWORD dwsize = 0; If (deviceiocontrol (hdev, ioctrl_buffer_size, null, 0, & dwsize, sizeof (dwsize), null, null) = false) {G Oto exit;} // allocate the cache g_vtbuf.resize (dwsize); // read data from the driver if (deviceiocontrol (hdev, ioctrl_get_buffer, null, 0, & g_vtbuf [0], g_vtbuf.size (), null, null) = false) {goto exit;} // print each data. // The Code cannot be compiled here, and the error "error c2712: cannot Use _ Try in functions that require object unwinding "for (STD: vector: iterator iter = g_vtbuf.begin (); iter! = G_vtbuf.end (); ITER ++) {printf ("% d/N", * ITER);} Bres = true; Exit: If (Bres = false) {g_vtbuf.clear ();} closehandle (hdev); leavecriticalsection (& g_csbuf); Return Bres ;}
In the last example, it is explained from another perspective that GOTO is not necessarily a weakness. In some specific environments, only it can save the code from the elegance.

At the end of the article, let's make a summary. To achieve the purpose of code elegance, we prefer _ Try; only when C ++ exceptions are used in the code, leading to conflicts with Seh, can we pick up the highly criticized Goto, to fulfill our purpose of elegance.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.