Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Jabor posted:

Mainly because feeling the need to jump out of a highly-nested loop while remaining in the same function usually indicates that the function is too busy and should be broken down into smaller, more digestible parts anyway. Alternatively, it could mean that you're doing error-prone manual cleanup of resources instead of doing it properly.

The goto itself is not necessarily "bad form", but it does indicate that something smelly is going on.

I disagree, in many cases nested loops make sense right where they are and don't need their own function. For example scanning over rows and columns of some 2d array.

Hubis posted:

I'd use a bool flag and "if (bBailout) break;" checks at the end of each loop, myself, but it depends on the code.

This sounds a lot like doing anything to avoid a goto.

Adbot
ADBOT LOVES YOU

Hubis
May 18, 2003

Boy, I wish we had one of those doomsday machines...

HappyHippo posted:

I disagree, in many cases nested loops make sense right where they are and don't need their own function. For example scanning over rows and columns of some 2d array.


This sounds a lot like doing anything to avoid a goto.

for tight loops that are algorithmically coupled already, I'd agree. For larger code or loops where there's some non-trivial side-effects that need to be managed, I like the flag because it puts each each layer in charge of its own execution -- it makes it harder for someone to add something to an outer-loop and not understand why it's not getting called because some inner loop is hitting a goto.

The "harmfulness" of a goto is proportional to the complexity of the surrounding code.

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Hubis posted:

for tight loops that are algorithmically coupled already, I'd agree. For larger code or loops where there's some non-trivial side-effects that need to be managed, I like the flag because it puts each each layer in charge of its own execution -- it makes it harder for someone to add something to an outer-loop and not understand why it's not getting called because some inner loop is hitting a goto.

The "harmfulness" of a goto is proportional to the complexity of the surrounding code.

Fair enough, so long as the flag has a relevant name.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

HappyHippo posted:

I disagree, in many cases nested loops make sense right where they are and don't need their own function. For example scanning over rows and columns of some 2d array.

Actually, "scan through this 2d array until you find something" is exactly the sort of thing that can be abstracted into a small function. You don't actually give a poo poo about the mechanics of iterating through the array, you only care about the test you're doing on each element and what you're going to do once you find what you're after.

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Jabor posted:

Actually, "scan through this 2d array until you find something" is exactly the sort of thing that can be abstracted into a small function. You don't actually give a poo poo about the mechanics of iterating through the array, you only care about the test you're doing on each element and what you're going to do once you find what you're after.

How is this any different than extracting every loop into its own function? There's no significant difference between looping over one variable or over two. Pulling it out to its own function is unnecessary.

Sex Bumbo
Aug 14, 2004

Jabor posted:

Actually, "scan through this 2d array until you find something" is exactly the sort of thing that can be abstracted into a small function. You don't actually give a poo poo about the mechanics of iterating through the array, you only care about the test you're doing on each element and what you're going to do once you find what you're after.

Moving things to other functions that are used exactly one time is also something that sort of confuses me. It's like reading a book but the author scrambled the pages for some reason, and they're telling you it's actually incredibly good this way.

Sure a decent IDE can jump to definitions, but they can also collapse code regions too.

Sex Bumbo fucked around with this message at 04:26 on Mar 8, 2016

Klades
Sep 8, 2011

Sex Bumbo posted:

Moving things to other functions that are used exactly one time is also something that sort of confuses me. It's like reading a book but the author scrambled the pages for some reason, and they're telling you it's actually incredibly good this way.

Sure a decent IDE can jump to definitions, but they can also collapse code regions too.

Ideally, the function name should be obvious enough that there's no need to actually look at the definition if you're trying to figure out what the code is doing anyway.
So it's more like reading a book, and instead of "He grabbed the handle, and turned. Pushing slightly, the large wood and metal slab moved forward on its hinge, allowing access to the space behind it" the guy wrote "And then he opened the door".

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

HappyHippo posted:

How is this any different than extracting every loop into its own function? There's no significant difference between looping over one variable or over two. Pulling it out to its own function is unnecessary.

Actually, just scanning over a single array to find a value is also something you want to extract into its own function.
(the function is called std::find_if)

Similarly, you're not actually searching through a two-dimensional array once. You're doing it dozens of times. You don't care about the mechanics of the search, you only really care about what your search condition is this time.

HappyHippo
Nov 19, 2003
Do you have an Air Miles Card?

Jabor posted:

Actually, just scanning over a single array to find a value is also something you want to extract into its own function.
(the function is called std::find_if)

Similarly, you're not actually searching through a two-dimensional array once. You're doing it dozens of times. You don't care about the mechanics of the search, you only really care about what your search condition is this time.

I don't know what you're talking about any more. Are you talking about extracting generic looping operations into functions like find_if (a good idea, although one that is more the domain of the standard library), or are you talking about extracting specific loops within procedures into their own functions (unnecessary)? Because your initial post sounded like the former:

Jabor posted:

Mainly because feeling the need to jump out of a highly-nested loop while remaining in the same function usually indicates that the function is too busy and should be broken down into smaller, more digestible parts anyway. Alternatively, it could mean that you're doing error-prone manual cleanup of resources instead of doing it properly.

The goto itself is not necessarily "bad form", but it does indicate that something smelly is going on.

Grocer Goodwill
Jul 17, 2003

Not just one kind of bread, but a whole variety.

Jabor posted:

Actually, just scanning over a single array to find a value is also something you want to extract into its own function.

Herb Sutter and Scott Meyers have completely corrupted the minds of an entire generation.

sarehu
Apr 20, 2007

(call/cc call/cc)
For loops are more practically editable as you maintain your code between now and infinity. You want to keep your code in a low-energy state so that there's low cost to making changes, adding logging statements and the like, and making improvements.

sarehu fucked around with this message at 05:59 on Mar 8, 2016

pseudorandom name
May 6, 2007

What's low energy code?

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

hackbunny posted:

I should have added an asterisk to that "nothing". Placement new is another easy way to skip destructors (I don't recommend using placement new, like ever)
Why is placement new special for skipping destructors? You can skip destructors with any 'new' you like, by not calling delete, just like you might skip calling the destructor with placement new.
Do you mean that you have to explicitly specify how to call the destructor if you're unique_ptr-ing a placement new?

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

roomforthetuna posted:

Why is placement new special for skipping destructors? You can skip destructors with any 'new' you like, by not calling delete, just like you might skip calling the destructor with placement new.
Do you mean that you have to explicitly specify how to call the destructor if you're unique_ptr-ing a placement new?

No placement delete, you have to call the destructor (!) yourself. Don't use placement new unless you're absolutely totally sure of what you're doing

Sex Bumbo
Aug 14, 2004

Klades posted:

Ideally, the function name should be obvious enough that there's no need to actually look at the definition if you're trying to figure out what the code is doing anyway.
So it's more like reading a book, and instead of "He grabbed the handle, and turned. Pushing slightly, the large wood and metal slab moved forward on its hinge, allowing access to the space behind it" the guy wrote "And then he opened the door".

You can collapse a code region and put a comment above it too though.

code:
// The following block of code does a thing
[+] [[Code hidden by IDE]]

or

[-] blah 
 |  blah 
 |  blah

You could also do this madness: cpp.sh/3b2wau

I can't really articulate why I don't like that.

Sex Bumbo fucked around with this message at 21:34 on Mar 8, 2016

sarehu
Apr 20, 2007

(call/cc call/cc)

pseudorandom name posted:

What's low energy code?

Maybe it's a high-energy state and I got the metaphor backwards. Anyway you can change your code from one state to another more easily if it's written using for loops and such, compared to if you use find_if or accumulate and such, where you have to do more janitoring to get it from one form to another. So if you write code with find_if, that's neat, in particular if it's less work in the first place (and IMO it often is, especially in non-C++ languages) -- but once you need to change it to something for which your higher-level function doesn't work, you should drop it and just have a honking for loop, or while loop. (And maybe some gotos, for garnish.)

Hubis
May 18, 2003

Boy, I wish we had one of those doomsday machines...

Sex Bumbo posted:

You can collapse a code region and put a comment above it too though.

code:
// The following block of code does a thing
[+] [[Code hidden by IDE]]

or

[-] blah 
 |  blah 
 |  blah

You could also do this madness: cpp.sh/3b2wau

I can't really articulate why I don't like that.

Well your code begins to look like Perl, for one

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

hackbunny posted:

No placement delete, you have to call the destructor (!) yourself. Don't use placement new unless you're absolutely totally sure of what you're doing
Sure, but don't use new either if you don't know what you're doing. That's even worse because you're introducing memory leaks when you neglect to clean up, whereas with placement new at least you're only leaking sub-objects. :)

Placement new is really no harder to handle than regular new, it just has less support in unique_ptr etc. and you're less likely to know about it because it doesn't tend to get taught. But if it did get taught, it isn't any harder to understand placement new + call the destructor than it is to understand new and delete.

(To be fair, when I mentioned this earlier I did have to look up unique_ptr to see if it's possible to scope-destruct things you placement-new'd - it is, but not trivial. I think you could fairly easily make a unique_placement_ptr template that would handle it correctly.)

b0lt
Apr 29, 2005

roomforthetuna posted:

(To be fair, when I mentioned this earlier I did have to look up unique_ptr to see if it's possible to scope-destruct things you placement-new'd - it is, but not trivial. I think you could fairly easily make a unique_placement_ptr template that would handle it correctly.)

code:
template<typename T>
auto unique_placement_ptr(T* t) {
  return std::unique_ptr<T, void(*)(T*)>(t, [](T* t) { t->~T(); });
}

Volguus
Mar 3, 2009

roomforthetuna posted:

Sure, but don't use new either if you don't know what you're doing. That's even worse because you're introducing memory leaks when you neglect to clean up, whereas with placement new at least you're only leaking sub-objects. :)

Placement new is really no harder to handle than regular new, it just has less support in unique_ptr etc. and you're less likely to know about it because it doesn't tend to get taught. But if it did get taught, it isn't any harder to understand placement new + call the destructor than it is to understand new and delete.

(To be fair, when I mentioned this earlier I did have to look up unique_ptr to see if it's possible to scope-destruct things you placement-new'd - it is, but not trivial. I think you could fairly easily make a unique_placement_ptr template that would handle it correctly.)

What would you suggest to do when dealing with a C API that accepts a struct as a parameter, and that struct has a "void* user_data" member? And then you need that user_data later on, when you're far away from the original scope when the struct was created and initialized?
Making an unique_ptr live that long would require quite a bit of refactoring plus not to mention leaking the API to the header and to whoever includes that. That can be solved with pimpl, but you are still left with the non-trivial refactoring problem.

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today
Sometimes refactoring is necessary to allow resource ownership to be explicitly encoded. Do it, preempt the otherwise nearly inevitable leaks, and try to plan ahead better next time.

Volguus
Mar 3, 2009

Ralith posted:

Sometimes refactoring is necessary to allow resource ownership to be explicitly encoded. Do it, preempt the otherwise nearly inevitable leaks, and try to plan ahead better next time.

I understand that and I agree ... usually ... but in this particular case the struct itself is the owner. What refactoring would do would destroy an otherwise very clean design with clearly defined boundaries. To leak that pointer outside the scope of the struct (in order to prolong its life) would essentially give ownership of that unique_ptr to a different entity who should not have any business in managing lifetime of those objects.
Essentially, from what I can see, I can either maintain the clean design, with the downside of having to do a new and later on a delete (all encapsulated in a higher order unique_ptr with a custom deleter, since ... that's life in C) or break the design only so that I don;t have to do new/delete.

Anyway, thanks for the input. Its been bugging me for a while that maybe I'm missing something, some better way.

Ralith
Jan 12, 2011

I see a ship in the harbor
I can and shall obey
But if it wasn't for your misfortune
I'd be a heavenly person today

Volguus posted:

I understand that and I agree ... usually ... but in this particular case the struct itself is the owner.
Well, who owns the struct? Is there anything stopping you from exposing a wrapped version of it with a destructor, move constructors, etc?

It's possible for a poorly designed or sufficiently foreign library to force you to write ugly code, but I've worked extensively with that type of struct-with-a-pointer C API in e.g. libuv and never had trouble applying safe(r) resource management with it.

Ralith fucked around with this message at 02:53 on Mar 9, 2016

Volguus
Mar 3, 2009

Ralith posted:

Well, who owns the struct? Is there anything stopping you from exposing a wrapped version of it with a destructor, move constructors, etc?
I own that struct. I control its lifespan.
I could wrap it I guess, it would be a solution. I just wrote a paragraph explaining my dilemma, removed it, I guess I better just show some code (changed to protect the innocent and my company from killing me. it's from memory anyway):

C++ code:
//c_header
struct C_struct
{
 int* pointer1; // some array of ints
 size_t pointer1_length;
 //other crap
 void* user_data;
};

int do_something_with_structs(C_struct** array_of_pointers, size_t length);


//my code
struct C_struct_array_deleter
{
//no idea if storing information in a deleter is kosher, but it's surely handy
size_t count;
C_struct_array_deleter(size_t count): count(count) {}
void operator()(C_struct** structs)
{
 for (size_t i = 0; i< count;++i)
 {
    C_struct* member = structs[i];
    delete[] member->pointer1;
    //here i delete user_data
    my_user_data* user_data = static_cast<my_user_data*>(member->user_data);
    delete user_data;
 }
 delete[] structs;
}
};

typedef std::unique_ptr<C_struct*[],C_struct_array_deleter> C_struct_array;

C_struct* create_struct(const unique_ptr<my_data>& some_data)
{
   C_struct* c_str = new C_struct;
   //other initizalization
   c_str->user_data = new my_user_data(some_numbers);
   return c_str;
}

C_struct_array create_struct_array(const std::vector<std::unique_ptr<my_data>>& data)
{
    std::vector<std::unique_ptr<my_data>> new_data = create_new_data_from_old_data(data); //this is actually a cartezian product done inside from the original data
    C_struct_array structs(new C_struct*[data.size()]{0},C_struct_array_deleter(data.size()));
    int index = 0;
    for(const auto& data_element : new_data)
    {
      structs[index] = create_struct(data_element,/*..and other params*/);
      index ++;      
    }
    return structs;
}


void now_my_actual_method_where_everything_gets_done(const std::vector<std::unique_ptr<my_data>>& data)
{
    //do things
    C_struct_array structs = create_struct_array(data);
    if(do_something_with_structs(structs.get(), structs.get_deleter().count))
   {
      throw my_exception("Something is rotten in Denmark");
    }

//etc, other things come here dealing with the structs created.

}

Now, looking again at it, hmm, there may be things that could be done I guess. Such as calling std::vector<std::unique_ptr<my_data>> new_data = create_new_data_from_old_data(data); in the main method and pass that to the create_struct_array, and then user_data could be just the value of my_data.get(). As it is though, the new_data vector has relatively short lifespan, which is nice.

Volguus fucked around with this message at 04:32 on Mar 9, 2016

Zerf
Dec 17, 2004

I miss you, sandman

Grocer Goodwill posted:

Herb Sutter and Scott Meyers have completely corrupted the minds of an entire generation.

I couldn't agree more - but I usually see it as a phase. Most of the better programmers I know has been through STL/boost/template phases, but come to their senses. Others seem to get stuck in that swamp forever and preach it every chance they get.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Zerf posted:

I couldn't agree more - but I usually see it as a phase. Most of the better programmers I know has been through STL/boost/template phases, but come to their senses. Others seem to get stuck in that swamp forever and preach it every chance they get.

I haven't done much more than maintenance C++ stuff in a long time, and these ports are 99% C or simple C++ (or Pro-C, god I hate Oracle), so I've lost touch. What do you mean by STL/boost/template phases, exactly?

xgalaxy
Jan 27, 2004
i write code

Ciaphas posted:

I haven't done much more than maintenance C++ stuff in a long time, and these ports are 99% C or simple C++ (or Pro-C, god I hate Oracle), so I've lost touch. What do you mean by STL/boost/template phases, exactly?

He means people who chase whatever trend or whatever. They like STL, and then boost is the cool thing now so we'll add all that poo poo. Etc. etc.

Ciaphas
Nov 20, 2005

> BEWARE, COWARD :ovr:


Oh, right, okay, I was looking too deeply into it and feeling vaguely worried :v:

bollig
Apr 7, 2006

Never Forget.
Hi, I'm learning C, just by doing the exercises in the K&R book. Everything's been going pretty smoothly until I got to the example where I make a function that passes a character array. Long story short the point of this program is to just type words and then when you're done, it prints the longest, I've typed it word for word from the book twice, and I've really tried to strip it down to the fundamentals, but I'm just getting conflicting type errors:

code:
#include <stdio.h>
#define MAXLINE 1000

int getline(char line, int maxline);

main()
{
  char line[MAXLINE];
  char longest[MAXLINE];

  int len, max, j;

  max = j = 0;
  

  while ((len = getline(line, MAXLINE)) > 0){
    printf("%s\n", line);
    j++;
  }

  printf("%s", line);
}

int getline(char s[], int lim)
{
  int c, i;

  for (i = 0; i < lim-1 && (c = getchar()) != '~' && c != '\n'; ++i)
    s[i] = c;
  if (c == '\n'){
    s[i] = c;
    ++i;
  }

  s[i] = '\0';
  return i;
}
And then here are the errors I'm getting:

code:
char_thing.c:4:5: error: conflicting types for ‘getline’
 int getline(char line, int maxline);
     ^
In file included from char_thing.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
                    ^
char_thing.c:6:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main()
 ^
char_thing.c: In function ‘main’:
char_thing.c:16:25: warning: passing argument 1 of ‘getline’ makes integer from pointer without a cast [-Wint-conversion]
   while ((len = getline(line, MAXLINE)) > 0){
                         ^
char_thing.c:4:5: note: expected ‘char’ but argument is of type ‘char *’
 int getline(char line, int maxline);
     ^
char_thing.c:11:12: warning: variable ‘max’ set but not used [-Wunused-but-set-variable]
   int len, max, j;
            ^
char_thing.c:9:8: warning: unused variable ‘longest’ [-Wunused-variable]
   char longest[MAXLINE];
        ^
char_thing.c: At top level:
char_thing.c:24:5: error: conflicting types for ‘getline’
 int getline(char s[], int lim)
     ^
In file included from char_thing.c:1:0:
/usr/include/stdio.h:678:20: note: previous declaration of ‘getline’ was here
 extern _IO_ssize_t getline (char **__restrict __lineptr,
I'm running Ubuntu 15.10, and I used gcc to compile it. When I've stripped it down, I have issues with functions that return an integar, but receive a character array, plain and simple. I'm going to go ahead and guess I'm missing something way important. Thanks.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
What's the difference between a (char) and a (char *)?

bollig
Apr 7, 2006

Never Forget.

Jabor posted:

What's the difference between a (char) and a (char *)?

So as I understand it, if it's char *, it's just in memory aaaand immutable, I think. Whereas char[] is an array with characters as the elements.

Oh, yeah, I fixed that. but my main issue is the second to last one:

code:
char_thing.c: At top level:
char_thing.c:24:5: error: conflicting types for ‘getline’
 int getline(char s[], int lim)

bollig fucked around with this message at 13:08 on Mar 18, 2016

astr0man
Feb 21, 2007

hollyeo deuroga
There's already a function called getline in the c standard library and its declared in stdio.h

bollig
Apr 7, 2006

Never Forget.

astr0man posted:

There's already a function called getline in the c standard library and its declared in stdio.h

AHA! This is it. Thank you.

ExcessBLarg!
Sep 1, 2001
So, to be clear, while K&R is a good book, it's also 28 year old. The built-in getline function was added to GNU libc as an extension and was later standardized in POSIX in the interim. If you want to follow the example in K&R, you'll have to call it "getline2" or something.

Doc Block
Apr 15, 2003
Fun Shoe

bollig posted:

So as I understand it, if it's char *, it's just in memory aaaand immutable, I think. Whereas char[] is an array with characters as the elements.

char * is a pointer to a char, that's all, but is usually understood to be a pointer to an array of chars (such as a NULL-terminated string). When it comes to function arguments, char[] is basically the same thing, only you're being more explicit in your code about what the intent is: you want an array of chars.

Either can be used to pass a char array, and in fact pretty much every C function that takes a string will take a char * or const char *.

I forget what the exact term is, but passing an array in C results in it devolving into a pointer and the pointer being passed instead.

Such as:
C code:
#include <stdio.h>
#include <string.h>

void array(char *arr)
{
	if(strlen(arr) > 3) {
		arr[2] = 'Z';
	}
}

int main(int argc, char **argv)
{
	char string[] = "Hello, world!"

	printf("Before: %s\n", string);

	array(string);

	printf("After : %s\n", string);

	return 0;
}

Doc Block fucked around with this message at 18:06 on Mar 18, 2016

Edison was a dick
Apr 3, 2010

direct current :roboluv: only

bollig posted:

AHA! This is it. Thank you.

You've also got inconsistencies between the first declaration and then the definition, you have one taking a single character, and another taking an array.

That Turkey Story
Mar 30, 2003

Doc Block posted:

I forget what the exact term is, but passing an array in C results in it devolving into a pointer and the pointer being passed instead.

decay

Hyvok
Mar 30, 2010
Can anyone recommend a good source or book on more low-level stuff (doesn't strictly have to be C/C++ related but that is kinda the higher context I'm working with)? I do a lot of embedded stuff and I feel I could do with more information on low-level stuff such as linking (memory layout etc.), compilation process, compiler optimizations etc. I have a good understanding of using C/C++ and I have a good understanding of CPU architectures but what happens in between could be clearer to me.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe

Hyvok posted:

Can anyone recommend a good source or book on more low-level stuff (doesn't strictly have to be C/C++ related but that is kinda the higher context I'm working with)? I do a lot of embedded stuff and I feel I could do with more information on low-level stuff such as linking (memory layout etc.), compilation process, compiler optimizations etc. I have a good understanding of using C/C++ and I have a good understanding of CPU architectures but what happens in between could be clearer to me.

The classic text here is the dragon book; it's been awhile since the second edition, but it's a pretty slow-moving field in the macro. I had a coworker who also recommended Linkers and Loaders, although I can't personally speak to it.

We're not going to meaningfully replace reading a book, but if you have any questions while reading those, feel free to ask here.

Adbot
ADBOT LOVES YOU

ExcessBLarg!
Sep 1, 2001
On the other end of that is the Computer Systems: A Programmer's Perspective textbook. It assume you're familiar with C, and it doesn't actually go into compilers proper, but covers a pretty broad range of Unix systems stuff. Textbooks are shockingly expensive, but even the first edition that came out nearly 15 years ago (and the one I'm actually familiar with) is still relevant, especially at a high level, and I think there's a more recent second edition that's probably pretty cheap now.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply