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
rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
C pointer arithmetic always operates in whole-element units. In lower-level terms, it's already scaled by the size of the element type. So in your original example:

C++ code:
  uint32_t *uartbase;
  uartbase = (uint32_t*) sb;
  uartbase += (HALUART_Log + (busnum << 2));
The last line here is actually adding (HALUART_Log + (busnum << 2)) * sizeof(uint32_t) to the value in uartbase because of this scaling. Don't worry, this is a very common misunderstanding for people starting from a hardware-centric perspective. So my version changes the type of sb to char*, adds HALUART_Log to that (which will therefore be scaled by sizeof(char), i.e. 1), casts to uint32_t*, and then indexes into that (which will now be scaled by sizeof(uint32_t), i.e. 4).

But if you have any control over this header at all, I would really recommend that you introduce a struct:

C++ code:
#define NUM_UARTS 5

struct workspace {
  /* other fields go here... */
  void *UARTBaseAddresses[NUM_UARTS];
};
If the offsets come from some canonical source, you can statically assert that you got the structure layout right by writing out declarations like:

C++ code:
#define assert_workspace_offset(field_name, offset) extern char field_name##offset_assertion[offsetof(struct workspace, field_name) == (offset) ? 1 : -1][/fixed]

assert_workspace_offset(UARTBaseAddresses, HALUART_Log);
/* etc. */
There's a more modern way of doing this if you can use C11 / C++11, but I don't want to make any assumptions about embedded toolchains.

Adbot
ADBOT LOVES YOU

General_Failure
Apr 17, 2005

rjmccall posted:

Don't worry, this is a very common misunderstanding for people starting from a hardware-centric perspective.
Thanks for catching me on that one.
I mean I used to write in C and to a much, much lesser extent C++, but it's method of handling pointers, including syntax always does my head in.

quote:

There's a more modern way of doing this if you can use C11 / C++11, but I don't want to make any assumptions about embedded toolchains.

According to the documentation it implements a decent subset of C99. So, no.
It's not embedded but the toolchain is commercial. Because of the end result of legacy it's incredibly difficult to avoid using it for initial boot and HAL. I tried that a couple of years ago using GCC. Not impossible (I think) but beyond my ability. It's a multi-faceted issue that has a nasty habit of propagating. Really surprising because the boot / HAL code is built as a separate binary then incorporated into the main image. But that's all beside the point.

I'd love to use a struct with a sprinkling of unions as an access method for the static workspace. In fact I'm pretty sure it'd work perfectly. The problem is maintainability. If the format of the static workspace is changed, the struct is accessing incorrect data unless it's updated by hand to match. While I hate using a big header full of #defines, it can be autogenerated from a script via the makefile if necessary.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
If you can autogenerate a header full of defines, you can autogenerate static asserts that will make sure your struct is accessing the right locations (and tell you to update it if it isn't).

General_Failure
Apr 17, 2005

Jabor posted:

If you can autogenerate a header full of defines, you can autogenerate static asserts that will make sure your struct is accessing the right locations (and tell you to update it if it isn't).

"I" would be an ancient perl script that somebody wrote that performs the magic. I just give it an input and output file and try to make sure none of the errors change the alignment. It's mostly just lines the script doesn't understand.

Dren
Jan 5, 2001

Pillbug
I never really write tmp stuff, is this ostream overload to print, comma separated, any type with const iterators cool or is it dumb

code:
template <class T, class = std::enable_if_t<std::is_object<decltype(std::declval<T>().cbegin())>::value>>
std::ostream& operator<<(std::ostream& os, const T& items) {
    auto cit = items.cbegin();
    auto cit_ahead = ++items.cbegin();
    auto increment_both = [&cit, &cit_ahead] { ++cit; ++cit_ahead; };
    for (; cit != items.cend() && cit_ahead != items.cend(); increment_both()) {
        os << *cit << ", ";
    }
    if (cit != items.cend()) {
        os << *cit;
    }
    return os;
}

int main() {
    const int intarr[] = { 9, 3, 6, 4, 8, 2, 1, 0, 7, 5 };
    std::list<int> list;

    for (auto i : intarr) {
        list.push_back(i);
    }

    list.sort(std::greater<int>());
    std::cout << list << std::endl;
    list.sort();
    std::cout << list << std::endl;
}

MutantBlue
Jun 8, 2001

I think it's easier to just print the first element and then iterate over the rest of the sequence printing a comma followed by the current element. That's one iterator and a simpler loop.

Dren
Jan 5, 2001

Pillbug

MutantBlue posted:

I think it's easier to just print the first element and then iterate over the rest of the sequence printing a comma followed by the current element. That's one iterator and a simpler loop.

that would be better, yeah.

Captain Cappy
Aug 7, 2008

Dren posted:

I never really write tmp stuff, is this ostream overload to print, comma separated, any type with const iterators cool or is it dumb

I kind of dislike the idea of overloading operator<< with a template like this. I feel like it it would be an overload collision waiting to happen. It sucks from an operator chaining perspective but actually naming the function something like outputAsCommaSeparatedList would be better I think. You could also write it as a function that doesn't take an ostream but returns a std::string instead so that you could still chain <<. The std::string version has the advantage of being more flexible as well, since there may be situations where you do not want to create an ostream just to turn this into a std::string.

- Using std::begin() and std::end() are probably better from an extendability standpoint as well.
- I think the function would be clearer if you did the comma differently. It's kind of confusing what was going on. There's a ton of ways to skin this cat but the following is pretty clear I think:

code:
using std::begin;
using std::end;
auto iterator = begin(container);
if (iterator != end(container))
{
    output << *(iterator++);
}
for (; iterator != end(container); ++iterator)
{
    output << ", " << *iterator;
}

Captain Cappy fucked around with this message at 05:29 on Dec 6, 2019

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Notably, std::string provides cbegin(), so this template will compete with the standard operator<<. It will probably lose on account of being less specialized — but then again it might not, because the standard operator<< is probably templated over the character type, so this will in fact be more specialized in at least one argument.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Potentially a better way to do this is to write a function that takes in your iterator, and returns something that would print the joined result when you write it to an ostream. So you would use it like:
code:

out << StrJoin(my_container, ", ");

In the simplest case you could just return a string (which would also make this useful in non-ostream contexts), but if you profiled things and determined that the extra allocation was unacceptable for your application then you could also return a custom type that just records the arguments, with an overloaded operator<< that actually does the work.

Xarn
Jun 26, 2015

Jabor posted:

Potentially a better way to do this is to write a function that takes in your iterator, and returns something that would print the joined result when you write it to an ostream. So you would use it like:
code:
out << StrJoin(my_container, ", ");
In the simplest case you could just return a string (which would also make this useful in non-ostream contexts), but if you profiled things and determined that the extra allocation was unacceptable for your application then you could also return a custom type that just records the arguments, with an overloaded operator<< that actually does the work.

This, because your implementation is nowhere near constrained enough.

Beef
Jul 26, 2004
Pointer arithmetic follows its sizeof(*ptr), so 32 bits to answer the question.
Use a char* if the offsets are in bytes.

Edit: img-timelined myself

Dren
Jan 5, 2001

Pillbug

Captain Cappy posted:

I kind of dislike the idea of overloading operator<< with a template like this. I feel like it it would be an overload collision waiting to happen.

rjmccall posted:

Notably, std::string provides cbegin(), so this template will compete with the standard operator<<. It will probably lose on account of being less specialized — but then again it might not, because the standard operator<< is probably templated over the character type, so this will in fact be more specialized in at least one argument.

You guys are correct. As implemented in my first post, a cout of a std::string is a collision.
code:
test.cpp:47:15: error: use of overloaded operator '<<' is ambiguous (with operand types 'std::ostream'
      (aka 'basic_ostream<char>') and 'std::string' (aka 'basic_string<char>'))
    std::cout << hello << std::endl;
    ~~~~~~~~~ ^  ~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.4.0/../../../../include/c++/7.4.0/bits/basic_string.h:6284:5: note: candidate
      function [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
    operator<<(basic_ostream<_CharT, _Traits>& __os,
    ^
test.cpp:21:15: note: candidate function [with T = std::__cxx11::basic_string<char, std::char_traits<char>,
      std::allocator<char> >, $1 = void]
std::ostream& operator<<(std::ostream& os, const T& items) {
              ^
1 error generated.
I presume if I overloaded operator<< with a specific print function for a container type like std::list the same issue would occur. Given that the normal way to interact with operator<< is to overload it for the specific type, this template thing I did is totally a booby trap.

Jabor's suggestion of a join function makes way more sense and is how this sort of thing is typically done anyway. I wrote a join function i/o manipulator but it didn't work since the relevant ostream overload is for an actual function pointer, not a std::function. So I added an overload for a std::function and it's all good:
code:
std::ostream& operator<<(std::ostream& os, std::function<std::ostream&(std::ostream&)> func) {
    return func(os);
}

template <class T, class = std::enable_if_t<std::is_object<decltype(std::declval<T>().cbegin())>::value>>
auto join(const T& items, const std::string& separator=", ") {
    return [&items, &separator](auto& os) -> auto& {
        auto cit = std::begin(items);
        if (cit != std::end(items)) {
            os << *cit++;
        }
        for (; cit != std::end(items); ++cit) {
            os << separator << *cit;
        }

        return os;
    };
}

int main() {
    const int intarr[] = { 9, 3, 6, 4, 8, 2, 1, 0, 7, 5 };
    std::list<int> list;

    for (auto i : intarr) {
        list.push_back(i);
    }

    list.sort(std::greater<int>());
    std::cout << join(list) << std::endl;
    list.sort();
    std::cout << join(list) << std::endl;

    std::string hello = "hello";
    std::cout << hello << std::endl;
}

Dren fucked around with this message at 17:29 on Dec 6, 2019

Volguus
Mar 3, 2009
I have 2 questions that I'd really love an answer to:

1. For nlohmann::json library, how would one go about defining an operator== for the float type (json::number_float_t which now is a double) so that it does an epsilon comparison (like they have in the documentation), but without actually changing json.hpp

2. Google's protobuf library has this class method definition:

code:
template <typename CType, enum FieldType DeclaredType>
  INL static bool ReadPrimitive(io::CodedInputStream* input, CType* value);
And it can be invoked like this:

code:
            int32_t val = 0;
            ::google::protobuf::internal::WireFormatLite::ReadPrimitive<int32_t,
                    ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(input,&val);
This works, and it does what one expects it to do.

Now, I'd like to move this call into another method, still templated, but I would like the template to not be of enum google::protobuf::internal::WireFormatLite::FieldType type, but a different enum or an int that can be converted to the WireFromatLite::FiledType type.

essentially, instead of:
code:
template<typename Type, enum ::google::protobuf::internal::WireFormatLite::FieldType DeclaredType>
void readPrimitive(somearguments....)
to have:
code:
template<typename Type, enum MyEnum DeclaredType>
void readPrimitive(somearguments....)
and then somehow, convert to the google type in there somehow.

Why? Because I would like to not include "google/protobuf/wire_format_lite_inl.h" in my header file and still declare readPrimitive there as a member of the class. As it stands the only way to achieve what I want is to declare and implement readPrimitive as a standalone function in the compilation unit. It works, but I was wondering if there is a better option.

I tried even with variadic templates, no luck:
code:
    template<typename Type, typename... Others>
    void readPrimitive(somearguments....);

Thank you.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

Volguus posted:

I have 2 questions that I'd really love an answer to:

1. For nlohmann::json library, how would one go about defining an operator== for the float type (json::number_float_t which now is a double) so that it does an epsilon comparison (like they have in the documentation), but without actually changing json.hpp

Don't. Having a non-transitive operator== is a terrible idea.

And regardless, you can't reasonably judge whether two floats are "close enough" to being the same without knowing what went in to producing those values.

Volguus posted:

2. Google's protobuf library has this class method definition:

code:
template <typename CType, enum FieldType DeclaredType>
  INL static bool ReadPrimitive(io::CodedInputStream* input, CType* value);
And it can be invoked like this:

code:
            int32_t val = 0;
            ::google::protobuf::internal::WireFormatLite::ReadPrimitive<int32_t,
                    ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(input,&val);
This works, and it does what one expects it to do.

Now, I'd like to move this call into another method, still templated, but I would like the template to not be of enum google::protobuf::internal::WireFormatLite::FieldType type, but a different enum or an int that can be converted to the WireFromatLite::FiledType type.

essentially, instead of:
code:
template<typename Type, enum ::google::protobuf::internal::WireFormatLite::FieldType DeclaredType>
void readPrimitive(somearguments....)
to have:
code:
template<typename Type, enum MyEnum DeclaredType>
void readPrimitive(somearguments....)
and then somehow, convert to the google type in there somehow.

Why? Because I would like to not include "google/protobuf/wire_format_lite_inl.h" in my header file and still declare readPrimitive there as a member of the class. As it stands the only way to achieve what I want is to declare and implement readPrimitive as a standalone function in the compilation unit. It works, but I was wondering if there is a better option.

I tried even with variadic templates, no luck:
code:
    template<typename Type, typename... Others>
    void readPrimitive(somearguments....);

Thank you.

You've seen the

code:
// This class is for internal use by the protocol buffer library and by
// protocol-complier-generated message classes.  It must not be called
// directly by clients.
bit on the WireFormatLite class, right?

If you're just looking for ways to read VarInts and stuff, why not just directly use the methods on CodedInputStream?

Volguus
Mar 3, 2009

Jabor posted:

Don't. Having a non-transitive operator== is a terrible idea.

And regardless, you can't reasonably judge whether two floats are "close enough" to being the same without knowing what went in to producing those values.

That is correct for a library like json. An application, a user of said library, can however make that call. I know what went into producing those values, and I know I can say float a and b are the same if abs(a-b)<some arbitrary value.
Why do I wanna do this? So I can say ASSERT_EQ(json1,json2) in my unit test. Can I just loop through the values and compare each, doing the appropriate thing for float values? Sure, but what's the fun in that?

Jabor posted:


You've seen the

code:
// This class is for internal use by the protocol buffer library and by
// protocol-complier-generated message classes.  It must not be called
// directly by clients.
bit on the WireFormatLite class, right?

If you're just looking for ways to read VarInts and stuff, why not just directly use the methods on CodedInputStream?

Yes I have seen that, yes I am aware of it, it was a conscious decision that I made. Should it in the future became untenable to maintain my code given the speed of the changes google implements to these classes, I will revisit my decision. For now, it serves my needs perfectly.
I do use CodedInputStream where it can be used. As you probably know, the 2 classes go well hand in hand, but are not a duplicate of each other. They do different things. I suspect they do not, however, make changes that often to those APIs, since they are being used by the generated classes. I could be wrong, only time will tell.

This is a personal project, born out of not doing advent of code this year. I am and will be the sole developer and user of said project. The aim is to be able to take a proto file and a data file (xml, json, yaml) and produce a binary protobuf message that can be sent over to a consumer (http, websocket, plain socket) with the further away goal of being able to invoke gRPC methods, if even possible. Of course, it makes sense that if I can write a protobuf message, I should be able to read one as well and produce some data file (xml, json, yaml).

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!

Volguus posted:

Why do I wanna do this? So I can say ASSERT_EQ(json1,json2) in my unit test. Can I just loop through the values and compare each, doing the appropriate thing for float values? Sure, but what's the fun in that?
Aren't you already looping through the values and comparing each, as part of that equality assertion?
I'd think the right thing here would be something like AssertThat(json1, ApproximatelyEquals(json2, optionalThresholdWithReasonableDefault))

(Proto MessageDifferencer already has this option, though for some reason the threshold isn't configurable: https://developers.google.com/proto...yEquals.details )

Ah, it's there in matchers though: https://github.com/google/nucleus/blob/master/nucleus/testing/protocol-buffer-matchers.h
code:
AssertThat(proto1, Approximately(EqualsProto(proto2), margin));

roomforthetuna fucked around with this message at 17:46 on Dec 15, 2019

Volguus
Mar 3, 2009

roomforthetuna posted:

Aren't you already looping through the values and comparing each, as part of that equality assertion?
I'd think the right thing here would be something like AssertThat(json1, ApproximatelyEquals(json2, optionalThresholdWithReasonableDefault))

(Proto MessageDifferencer already has this option, though for some reason the threshold isn't configurable: https://developers.google.com/proto...yEquals.details )

Ah, it's there in matchers though: https://github.com/google/nucleus/blob/master/nucleus/testing/protocol-buffer-matchers.h
code:
AssertThat(proto1, Approximately(EqualsProto(proto2), margin));

Well yes, the operator== does that which is invoked by the ASSERT_EQ. I'm not quite sure how would a protobuf MessageDifferencer help me when comparing json objects.

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!

Volguus posted:

Well yes, the operator== does that which is invoked by the ASSERT_EQ. I'm not quite sure how would a protobuf MessageDifferencer help me when comparing json objects.
It would help as a model of doing it a way that isn't a coding horror.

MrMoo
Sep 14, 2000

Volguus posted:

1. For nlohmann::json library, how would one go about defining an operator== for the float type (json::number_float_t which now is a double) so that it does an epsilon comparison (like they have in the documentation), but without actually changing json.hpp

I’d also note that what some epsilon documentation does not note is that you always need TLC with greater than and less than operators: if a ~= b then a > b or a < b may also be true. You must always invoke the epsilon comparison first and thus also override the “>” and “<“ operators for a correct result.

peepsalot
Apr 24, 2007

        PEEP THIS...
           BITCH!

Sorry if this is somewhat offtopic, but I have a wacky side project where I'm trying to make some nice library of "standard" algorithms and utility functions for a niche, domain-specific language.

Its a functional programming language and wholly unrelated to C++ (well, the interpreter itself is implemented in C++, but I'm not talking about altering the language, just creating userspace libs in this lang).

Anyways, since I'm probably most familiar with C++, I figured that STL <algorithm> might be a good starting point for ideas to attempt implementing.

My data types are a bit restricted; no user-defined types really, and the only "sequences" or "containers" are essentially:
1) strings (can access individual characters as elements), and
2) vectors (random access, but every element is a basically Variant that can be any other type).
There's no builtin concept of iterators; I could hack something together that imitates them using some functional tricks, but it doesn't really seem worth it. So I started implementing some common algorithm functions to just take first, last *indices* instead of *iterators* when operating on sequences.

I also just recently started learning a bit about C++20 ranges, which seem much nicer than iterator based stuff, and I like the lazy evaluation aspect of them, so I'm wanting to incorporate some of those ideas.
I've found cppreference.com very useful as a reference for <algorithm>, but its still very incomplete for <ranges>.

So I guess my TL;DR question is: if there's any recommended reference to quickly get the gist of ranges from my weird standpoint, where I'm just sort copying ideas but not really using them directly in C++.
Is just reading the official standard proposals my best bet?

Also interested in any general discussion of perceived merits/flaws, and what people consider the most useful parts of <algorithm>, <ranges>, maybe a bit of <functional>(which I'm also not very familiar with), or other parts I should be looking at? Or is the overall idea of shoe-horning C++ stuff into this functional language terribly backwards and ill-advised?

Dawncloack
Nov 26, 2007
ECKS DEE!
Nap Ghost
Hello friends. I am stumped in something that's probably silly.

code:
typedef uint32_t (*this_method)(struct SoundStateKeeper *);

typedef struct SoundStateKeeper
    {
        uint32_t thisStateValue; 
        uint32_t nextState;
        this_method current_state_funcP;
    }
I have this two declarations. I am making C "objects". The problem is, whichever I put first, the compiler tells me that the other one is trying to implicitly declare the other.If I start with the function pointer, the struct of the function it points to hasn't been declared yet. Problem.
If I declare the struct first, it tells me it doesn't know what this_method is.

This is obviously a job for THIS. But I am working in embedded C and want to be careful and not do any monkey business.

It's late, I'm super jetlagged and I am getting up to work soon. Any elegant/blindingly obvious suggestions?

TIA

pseudorandom name
May 6, 2007

Stick struct SoundStateKeeper; above the this_method typedef.

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Just writing struct SoundStateKeeper in the declaration is supposed to have that effect anyway. Do you have some weird warning on?

Zopotantor
Feb 24, 2013

...und ist er drin dann lassen wir ihn niemals wieder raus...

Dawncloack posted:

Hello friends. I am stumped in something that's probably silly.

code:
typedef uint32_t (*this_method)(struct SoundStateKeeper *);

typedef struct SoundStateKeeper
    {
        uint32_t thisStateValue; 
        uint32_t nextState;
        this_method current_state_funcP;
    }
I have this two declarations. I am making C "objects". The problem is, whichever I put first, the compiler tells me that the other one is trying to implicitly declare the other.If I start with the function pointer, the struct of the function it points to hasn't been declared yet. Problem.
If I declare the struct first, it tells me it doesn't know what this_method is.

This is obviously a job for THIS. But I am working in embedded C and want to be careful and not do any monkey business.

It's late, I'm super jetlagged and I am getting up to work soon. Any elegant/blindingly obvious suggestions?

TIA

The scope of a struct name declared in a prototype is only that prototype .

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
Oh, right, this is one of those rules that C just gets wrong (in the sense that it's not what people want or expect); C++ gets it right.

qsvui
Aug 23, 2003
some crazy thing

peepsalot posted:

So I guess my TL;DR question is: if there's any recommended reference to quickly get the gist of ranges from my weird standpoint, where I'm just sort copying ideas but not really using them directly in C++.
Is just reading the official standard proposals my best bet?

You could try looking at the range-v3 library which, according to the website, is the basis for C++20 ranges.

Twerk from Home
Jan 17, 2009

This avatar brought to you by the 'save our dead gay forums' foundation.
I'm looking for a recommended IDE or refactoring tool that could help me un-gently caress codebases that are using namespace std; that are neglected for a few years, and then now running into namespace collision with the stdlib. The quick and dirty is to rename things so they don't collide, but I think it would save future pain to get out of std namespace and be clear where things are coming from.

Any suggestions?

Raenir Salazar
Nov 5, 2010

College Slice
Does anyone have any idea how to use the imagemagick library to conveniently create an image from a 2D array or vector (Using a struct to hold RGB(A) data)? I'm flummoxed I've seen no documentation as to how its done and only one post on stackoverflow that doesn't work for me.

This would at least be a little more convenient but the output is corrupt and unopenable

Ideally just want a 2D array of rgba structs to feed into image magicks read function.

There's a constructor with this signature documentation here:

code:
const size_t width_,
const size_t height_,
std::string map_,
const StorageType type_,
const void *pixels_
and a "read" method with this signature:

code:
const size_t width_, const size_t height_, std::string map_, const StorageType type_, const void *pixels_
code:
Read image based on an array of image pixels. The pixel data must be in scanline order top-to-bottom. The data can be character, short int, integer, float, or double. Float and double require the pixels to be normalized [0..1]. The other types are [0..MaxRGB].  For example, to create a 640x480 image from unsigned red-green-blue character data, use
  image.read( 640, 480, "RGB", CharPixel, pixels );

The parameters are as follows:
 

width_	Width in pixels of the image.
height_	Height in pixels of the image.
map_	This character string can be any combination or order of R = red, G = green, B = blue, A = alpha, C = cyan, Y = yellow M = magenta, and K = black. The ordering reflects the order of the pixels in the supplied pixel array.
type_	Pixel storage type (CharPixel, ShortPixel, IntegerPixel, FloatPixel, or DoublePixel)
pixels_	This array of values contain the pixel components as defined by the map_ and type_ parameters. The length of the arrays must equal the area specified by the width_ and height_ values and type_ parameters.
I'm not sure if I would have to convert a 2D array to a 1D array to begin to feed it into that method regardless; I have been able to make a char array but I need width times height times three and this is inconvenient for me and would prefer a more straight forward and intuitive solution.


So it'd be nice if being able to store RGB data in a struct worked, and then storing as a 2D array would be nice after that. But I'll settle for having a struct work fine.

code:
typedef struct
{
  int r,g,b;
} __attribute__((__packed__))
rgbs;
This doesn't work for me at all, visual studio throws a syntax error with this.

astr0man
Feb 21, 2007

hollyeo deuroga

Raenir Salazar posted:

This doesn't work for me at all, visual studio throws a syntax error with this.

I think
code:
__attribute__((__packed__))
is a gcc/g++ extension, you probably need to use
code:
#pragma pack(1)
in VS

https://docs.microsoft.com/en-us/cpp/preprocessor/pack?redirectedfrom=MSDN&view=vs-2019

astr0man fucked around with this message at 17:44 on Jan 20, 2020

Raenir Salazar
Nov 5, 2010

College Slice

astr0man posted:

I think
code:
__attribute__((__packed__))
is a gcc/g++ extension, you probably need to use
code:
#pragma pack(1)
in VS

https://docs.microsoft.com/en-us/cpp/preprocessor/pack?redirectedfrom=MSDN&view=vs-2019

Thanks! I tried it but sadly that doesn't appear to resolve the issue of my png file being unopenable. :(

code:
typedef uint16_t WORD;

#pragma pack(push, 1)
typedef struct mRGB {
	WORD red;
	WORD green;
	WORD blue;
};
#pragma pack(pop)

//... somewhere down here is main
	std::vector<mRGB> rgbv;

	mRGB rgbs;

	std::default_random_engine generator;
	std::uniform_int_distribution<int> distribution(0, 255);
	auto mColour = std::bind(distribution, generator);

	for (int i = 0; i < 512 * 512; i++) {
		rgbs.red = mColour();
		rgbs.green = mColour();
		rgbs.blue = mColour();

		rgbv.push_back(rgbs);
	}

	Magick::Blob blob(&rgbv[0], rgbv.size() * sizeof(rgbv[0]));

	Magick::Image image;
	//image.magick("png"); // this doesnt seem to matter
	image.size("512x512");
	image.magick("RGB");	
	image.read(blob);

Absurd Alhazred
Mar 27, 2010

by Athanatos

Raenir Salazar posted:

Thanks! I tried it but sadly that doesn't appear to resolve the issue of my png file being unopenable. :(

code:
typedef uint16_t WORD;

#pragma pack(push, 1)
typedef struct mRGB {
	WORD red;
	WORD green;
	WORD blue;
};
#pragma pack(pop)

//... somewhere down here is main
	std::vector<mRGB> rgbv;

	mRGB rgbs;

	std::default_random_engine generator;
	std::uniform_int_distribution<int> distribution(0, 255);
	auto mColour = std::bind(distribution, generator);

	for (int i = 0; i < 512 * 512; i++) {
		rgbs.red = mColour();
		rgbs.green = mColour();
		rgbs.blue = mColour();

		rgbv.push_back(rgbs);
	}

	Magick::Blob blob(&rgbv[0], rgbv.size() * sizeof(rgbv[0]));

	Magick::Image image;
	//image.magick("png"); // this doesnt seem to matter
	image.size("512x512");
	image.magick("RGB");	
	image.read(blob);


I think what you want is in the "Low-Level Image Pixel Access" of the documentation you linked to. Blobs aren't the right way to go because they're supposed to already have encoding information (for PNG that would be a bunch of headers and metadata with the pixel depth, resolution, etc). It's giving you garbage because an RGB array is not a valid PNG file.

Doc Block
Apr 15, 2003
Fun Shoe
I have a question:

In my little 2D game, it basically has (simplified)
C++ code:
class GameObject;    // has a member function named update
...
std::vector<GameObject *> objectsThatWantUpdates;
...
void GameEngine::startUpdatingObject(GameObject *object);   // adds it to objectsThatWantUpdates
void GameEngine::stopUpdatingObject(GameObject *object);   // removes it from objectsThatWantUpdates
...
for(auto object : objectsThatWantUpdates) {
    // object can create new objects in its update function and call startUpdatingObject(), or call stopUpdatingObject() for itself or other objects
    // which will cause problems since we're iterating over objectsThatWantUpdates
    object->update(timeSinceLastFrame)
}
What's the best way to deal with this? To handle a std::vector potentially being modified multiple times, both adding and removing objects, while it's being iterated over?

My first thought is to add a flag that indicates when the vector is being iterated over, and modify startUpdatingObject() and stopUpdatingObject() so that if it's set they instead push the game object onto another vector (objectsToAdd and objectsToRemove, respectively), and then after iterating through objectsThatWantUpdates add/remove the contents of objectsToAdd/objectsToRemove from it.

Another thought was a doubly linked list instead of a vector, but that seems like it'd be slower to iterate over.

Doc Block fucked around with this message at 23:13 on Jan 24, 2020

rjmccall
Sep 7, 2007

no worries friend
Fun Shoe
It is going to substantially simplify your life if there’s a hard rule that the game engine doesn’t hold any references to an object, and never calls update on it, after stopUpdatingObject is called. If you need to support reentrance here, I would suggest just iterating by index up to a stashed limit instead of the current size, making startUpdatingObject append to the end, and making stopUpdatingObject nil out entries instead of removing them when an update is in-flight. You can teach update to just remove nil entries when it sees them.

Subjunctive
Sep 12, 2006

✨sparkle and shine✨

I was going to suggest making a copy of the vector to iterate over, but it depends on what you want to happen if an unvisited element is removed, and if you want iteration to see elements added during iteration. And obviously if the vector is enormous then that might be too expensive, but it seems like it would be a lot simpler to reason about until you needed to optimize it further.

If mutation during iteration is rare, you could lazily switch to a copy of the remainder of the vector when something first tries to add or remove. I did that in a language engine at one point, though the vectors in question were typically smaller than 50 elements, and mutation was a less than 1% case on our instrumented workloads.

Dren
Jan 5, 2001

Pillbug
If the primary use case for objectsThatWantUpdates is to be iterated over while being modified std::list seems more appropriate than std::vector since std::list iterators are not invalidated by adds or removes (unless the element the iterator refers to is removed). This is no problem for adds, either put them on the front (if they are not to be acted on this loop) or the back (if they are to be acted on this loop). Removes are trickier because the current iterator could be removed. Instead of removing it immediately add the iterator to it to a list of zombies and flag a field in GameObject to mark it dead. In other spots, check the field before acting on it then at the end of the update loop remove the zombies from the main list.

UraniumAnchor
May 21, 2006

Not a walrus.

Doc Block posted:

What's the best way to deal with this? To handle a std::vector potentially being modified multiple times, both adding and removing objects, while it's being iterated over?

It might help to try and think of your state block in a very transactional way, i.e. while your game objects are updating, they don't actually make changes to any other objects, only query other objects for their current state.

If you want to add a new object, create it and add it to a queue, but not the "real" list.

If you want to delete an object, just set a flag on said object.

At the end of the update loop, process the add queue, and delete any flagged objects.

If you want to take this a step further, you can even decide that objects can't actually update themselves during the update loop, only decide what they want their update to be, and then process THAT afterwards, alongside the additions/deletions. One advantage of this approach is that you don't have state wobbling around while your objects are trying to decide what the world looks like and react to it appropriately.

As an example of this sort of behavior, Box2D doesn't let you add/remove physics objects while it's resolving collisions, so if you want to, say, remove a wall because a meteor hit it, you have to remember that somehow and process it later after the physics step has resolved.

Doc Block
Apr 15, 2003
Fun Shoe
rjmccall: the only time stopUpdatingObject will be called is when the object is being removed, and then no more references to it are stored. Totally agree that this makes things way simpler.

Most game objects are static and don’t need to update every frame. It’s hard to imagine the objectsThatWantUpdates vector being larger than 30-50 elements.

Iterating over the vector by index and nulling out removed objects instead of immediately removing them, then pruning null objects when seen is so simple I’m embarrassed I didn’t think of it already 👍

Subjunctive: I don’t want objects getting updates after they’ve been scheduled for removal, even if they haven’t been visited yet. Getting visited on the same frame they’re added is probably OK.

Dren: I thought about using a linked list, but mostly objectsThatWantUpdates will just be iterated over. It’ll need to be modified during iteration sometimes, but at worst will still probably have triple digit frame counts between modifications.

UraniumAnchor: the first part of your suggestion is similar to what I had first thought (a separate vector of objects to add/remove) but rjmccall's suggestion is simpler and what I've decided to go with. The second part of your suggestion seems a lot more robust, but it'll have to wait for the next game.

Thanks everyone!

Doc Block fucked around with this message at 09:20 on Jan 25, 2020

Dren
Jan 5, 2001

Pillbug
I wrote a quick bench to verify that list takes longer to iterate than vector. I was a little surprised that it did, about twice as long. But I need to double check my bench to make sure it’s accurate.

Adbot
ADBOT LOVES YOU

Xarn
Jun 26, 2015
Why would you need to verify that?

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