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
more falafel please
Feb 26, 2005

forums poster

OddObserver posted:

That's sort of a problem, isn't it? Like if you actually care about safety and are going to put in an effort to switch to a C++ reskin suddenly going all the way to something like Rust starts being attractive?

I think it's an easier sell than learning a completely new language and environment, and dealing with all the interop. Not today, obviously, but in the next 5 years or so if the idea gets refined a bit.

Adbot
ADBOT LOVES YOU

cheetah7071
Oct 20, 2010

honk honk
College Slice
fortunately I've been able to avoid this issue of passing unique pointers so far because every time I've wanted to construct a member outside of the object, it's because I want it to be usable in multiple places, so shared_ptr is correct for reasons entirely unrelated to rvalue nonsense

Twerk from Home
Jan 17, 2009

This avatar brought to you by the 'save our dead gay forums' foundation.

Xerophyte posted:

My concern is that you said "I want to pass in a ready-to-go std::istream, and I do want ownership of it to move to the consumer." istream has disabled moves specifically to ensure it is not possible to do that, because it is not in general a safe operation. An istream may be a system resource that cannot be locally owned. Moving is only enabled for classes derived from istream that represent something that can be owned and have its ownership transferred, notably ifstreams.

The discussion gave me the impression that you intended to get around this by passing around std::unique_ptr<std::istream>s, which technically will work as long as you only use them for streams that can be moved (or, well, constructed/deleted locally). It smells and I would try to not do that, though.

When I came into this, the proof of concept was plain std::ifstream only, no polymorphism. The whole world handles these files gzip compressed, and my lab uses a lot of zstd compression, so I used boost::iostreams::filtering_stream, but wanted to be able to leave parts of the application untouched and still using plain ifstreams. boost filtering_streams are istreams, but not ifstreams, and all that I need from any of them is the istream interface.

You explained pretty clearly why I can't assume every istream is move-able, and I guess I should just change all of these to boost filtering_streams instead. I just came in with Java brain and saw "We are only using these as istream? Let's just type the whole thing std::istream." They're all only std::ifstream or boost::iostreams::filtering_istreams anyway.

Side note: Is boost::iostreams still the right thing to be reaching for for basic compression / decompression of gzip and zstd? Bonus points for something that uses Intel isa-l instead of zlib, as it's night-and-day faster and we use igzip. I've been thinking about writing a boost filter that links against isa-l myself, but my group tends to just use zstd instead. I feel like other users that are using gzip would sure appreciate it, though.

Xarn posted:

Calling sink1 will not always zero-out the passed pointer, because std::unique_ptr<Data>&& is a reference and thus the caller's unique ptr instance will only be modified after the coinflip. This is different from sink2 where caller's pointer is always moved-out, because it has to be moved into the function before the coinflip happens.

This is why I prefer taking these types by value, because then I can be sure that when the function exits, no matter the way it does so, my input has been already taken care of and I can't see the old value. This makes it harder to get to inconsistent state.

Oh wow, I would never want a pointer to not be moved into the function when calling it with std::move, so you've convinced me that value semantics are safer for this situation. I can already think of a handful of exciting ways that && and not moving it could explode spectacularly.


cheetah7071 posted:

I'll put up with all the C++ nonsense just to have namespaces and std::vector

Tired: C-with-classes style code
Wired: C with vector

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

Twerk from Home posted:

When I came into this, the proof of concept was plain std::ifstream only, no polymorphism. The whole world handles these files gzip compressed, and my lab uses a lot of zstd compression, so I used boost::iostreams::filtering_stream, but wanted to be able to leave parts of the application untouched and still using plain ifstreams. boost filtering_streams are istreams, but not ifstreams, and all that I need from any of them is the istream interface.

You explained pretty clearly why I can't assume every istream is move-able, and I guess I should just change all of these to boost filtering_streams instead. I just came in with Java brain and saw "We are only using these as istream? Let's just type the whole thing std::istream." They're all only std::ifstream or boost::iostreams::filtering_istreams anyway.

Side note: Is boost::iostreams still the right thing to be reaching for for basic compression / decompression of gzip and zstd? Bonus points for something that uses Intel isa-l instead of zlib, as it's night-and-day faster and we use igzip. I've been thinking about writing a boost filter that links against isa-l myself, but my group tends to just use zstd instead. I feel like other users that are using gzip would sure appreciate it, though.

Oh wow, I would never want a pointer to not be moved into the function when calling it with std::move, so you've convinced me that value semantics are safer for this situation. I can already think of a handful of exciting ways that && and not moving it could explode spectacularly.

Tired: C-with-classes style code
Wired: C with vector

stb has dynamic arrays
https://nothings.org/stb_ds/

Ihmemies
Oct 6, 2012

Sweeper posted:

If you are new to cpp bookmark https://www.cppreference.com or add a quick search so you can type “cpp std::map” into url bar thing to lookup docs. I look things up all the time because c++ is inconsistent and many apis work in ways which make sense in the context of the standard, but aren’t intuitive. It’s a great site, bless the maintainers.

Yes. Our course even had some homework regarding using CPPref. I just forget to do this often enough.

Most of the Book was already presented ready for us. File reading, commandline handling etc. Only the implementation of Book classes methods was left for us. They gave us a ready struct we need to use for the chapters. I managed to do this not quite yet finished or even tested implementation yesterday: https://pastebin.com/uPDac0fA

At least some kind of recursion and shared pointers feel a lot easier to use when you can do whatever by yourself, and are not constrained too much by the strict limits set by coursework.

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!

Ihmemies posted:

Most of the Book was already presented ready for us. File reading, commandline handling etc. Only the implementation of Book classes methods was left for us.
Is that Params monstrosity part of what they give you? Jesus.

OddObserver
Apr 3, 2009

quote:

std::shared_ptr<Chapter> parentChapter_ = nullptr;
std::vector<std::shared_ptr<Chapter>> subchapters_;

...I have questions.

Xarn
Jun 26, 2015
An N-ary tree with cyclic references, what could possibly go wrong.

Ihmemies
Oct 6, 2012

roomforthetuna posted:

Is that Params monstrosity part of what they give you? Jesus.

Yes. Function parameters are given inside that params variable. The Chapter struct was given to us. Initially they were regular pointers like this:

code:
Chapter* parentChapter_ = nullptr;
std::vector<Chapter*> subchapters_;
but I changed them to smart pointers like below. They said we can change existing regular pointers to smart pointers if we want to. I'm not sure which smart pointer I should use so I randomly rolled shared_ptr and it seems to work at least. I think I won't be very good at freeing memory, so I hope that I can avoid freeing memory manually by using smart pointers instead, like this:

code:
std::shared_ptr<Chapter> parentChapter_ = nullptr;
std::vector<std::shared_ptr<Chapter>> subchapters_;
Then based on the chapter data we have to implement printouts to look like this:

code:
Book> contents
- 1. Introduction to the Basic Concepts ( 2 )
-   1. Definitions ( 5 )
-   2. The Basics ( 3 )
- 2. Fundamentals of Software Change ( 1 )
-   1. Software Change ( 2 )
-     1. Classification of Changes ( 2 )
-       1. Corrective Change ( 2 )
-       2. Adaptive Change ( 2 )
-       3. Perfective Change ( 4 )
-       4. Pretentive Change ( 3 )
-     2. The Importance of Categorizing Software Changes ( 6 )
-   2. Lehmans Laws ( 3 )
- 3. Program Understanding ( 3 )
-   1. Factors That Affect Understanding ( 2 )
-     1. Naming Style ( 3 )
-     2. Comments ( 2 )
-     3. Decomposition Mechanism ( 2 )
-   2. Documentation ( 4 )
- 4. Summary ( 3 )
Or

code:
Book> parents Adaptive 3
Adaptive has 3 parent chapters:
Classification
Fundamentals
Software Change
Etc.

Xarn posted:

An N-ary tree with cyclic references, what could possibly go wrong.

I am glad I have no idea, because I don't know what is an N-ary tree, or what are cycling references, or even what can possibly go wrong :v:

Ihmemies fucked around with this message at 21:22 on Nov 17, 2022

cheetah7071
Oct 20, 2010

honk honk
College Slice
the distinction between unique_ptr and shared_ptr is:

do I want this object to always be deleted when the pointer goes out of scope?

if yes, use unique_ptr. If you need it to stick around because other parts of the code are potentially still using it, use shared_ptr.

I don't know which you need for this specific task, but the distinction is pretty simple

weak_ptr is a kind of weirder one and I think if you're struggling with the concept of pointers to begin with, you probably won't be using it at all

Volguus
Mar 3, 2009

cheetah7071 posted:

the distinction between unique_ptr and shared_ptr is:

do I want this object to always be deleted when the pointer goes out of scope?

if yes, use unique_ptr. If you need it to stick around because other parts of the code are potentially still using it, use shared_ptr.

I don't know which you need for this specific task, but the distinction is pretty simple

weak_ptr is a kind of weirder one and I think if you're struggling with the concept of pointers to begin with, you probably won't be using it at all

I look at them in terms of ownership. Who "owns" this pointer?

- One object at a time - unique_ptr
- Everybody and their grandmother? - shared_ptr
- weak_pr for when I need to know that my object is dead already by the time a callback is called. And for that I need shared_ptr. And, of course, when the object needs to keep itself alive, then also shared_ptr.

Ihmemies
Oct 6, 2012

Thanks! One reason also was (now when I'm re-reading this week's material) that they said "On this course, we will only get to know the type shared_ptr.". So it should be good enough for now. I guess I'd have to manuallydo a bunch of deleting in class destructor and maybe methods if I used regular pointers instead, and analyze with Valgrind if I succeed in that task or not.

OddObserver
Apr 3, 2009
Of more immediate significance: if your shared_ptr's form a cycle, they'll never get cleaned up.

(I apologize if my earlier comment was too snarky, I thought it was the code that you were provided...)

Edit: oh boy, they have you using shared_ptr and are not explaining cycles?

Let's see, the arrow here are shared_ptr's, and I'll represent the Chapter objects with their names:

pre:
Introduction to the Basic Concepts ( 2 )
          |                         /\
          |                          |
  subchapters_[0]                parentChapter_ 
          |                          |
          |                          |
         \/                          |
Definitions   ------------------------

See how if you follow the arrows you can keep going around and around? That's called a cycle.
The way shared_ptr works is basically by freeing memory when the number of incoming arrows is zero. And here, you'll always have incoming arrows, as parent points to child and child to parent.

OddObserver fucked around with this message at 21:52 on Nov 17, 2022

Ihmemies
Oct 6, 2012

OddObserver posted:

Of more immediate significance: if your shared_ptr's form a cycle, they'll never get cleaned up.

(I apologize if my earlier comment was too snarky, I thought it was the code that you were provided...)

So if the program quits they stay in memory? Or those things stay in memory until the program closes? Is it bad if they stay in memory while the program is running?

I quickly read about trees and this page suggested to do first child/next sibling data structure: https://www.geeksforgeeks.org/generic-treesn-array-trees/

I guess that is impossible at this situation but from looking at it it looks more efficient?

Maybe I should replace the smart pointers with regular pointers and uhh do some manual deletion... :shepicide: Or maybe uhhhh make the uhh parent as a regular pointer and delete those in class destructor?

Ihmemies fucked around with this message at 21:54 on Nov 17, 2022

Twerk from Home
Jan 17, 2009

This avatar brought to you by the 'save our dead gay forums' foundation.

Ihmemies posted:

So if the program quits they stay in memory? What kind of sense that makes?

No, if you close the process, it will go away.

However, you will leak memory while it is running, as in any cycles of shared_ptrs pointing at each other will keep the pointed objects alive as long as the process runs. You have to manually break cycles with weak_ptrs somewhere in the cycle, which will not keep the pointed-at object around.

cheetah7071
Oct 20, 2010

honk honk
College Slice
If you're used to python, shared_ptr should be pretty intuitive to you because it's how python objects work. An object might exist in multiple places (it might be a member of multiple objects, it might be declared in one function, then used an argument in another, etc). When all of those places pass out of scope, the garbage collector cleans it up--or, in C++ terms, the object's destructor is called

Handling all of that is a bit slow though so unique_ptr exists for the case where you can guarantee that the object only exists in one place. It's a bit faster, and can prevent bugs in scenarios where you accidentally put an object in two places at once without intending to, because that code won't even compile

The circular references thing exists in python as well, though I think more modern versions of it have a complicated garbage collector that tries to detect them? It's been a while since I used python so I'm not sure. It occurs when you have a scenario like, object A contains object B as a member, and object B contains object A as a member. Because neither of them have 0 references, neither can be destroyed. Shared_ptr has the same risks if you aren't careful

Ihmemies
Oct 6, 2012

Well all the the chapter data is needed as long as the created book class object? instance? whatever it's called is alive, since the data is read only once from file.

So I guess it's ok that the shared pointers keep existing in memory, as long as the instance is alive and needed. In class destructor, do I need to iterate through the all the chapters and manually delete the pointers, or change parentchapter type to weak pointer or something?

Seems they explained the cycles (I looked) but I forgot already about it. My memory is very bad in general.

Or should I change the parentchapters to unique pointers or something.. hmm.. I need to study more.

cheetah7071
Oct 20, 2010

honk honk
College Slice
You almost never need to manually write a destructor. The default one covers you 99% of the time. The biggest exceptions in aware of are if you need to do something other than destroy objects for some reason, or if you're interfacing with C code and thus working with objects that don't have destructors

Ihmemies
Oct 6, 2012

cheetah7071 posted:

You almost never need to manually write a destructor. The default one covers you 99% of the time. The biggest exceptions in aware of are if you need to do something other than destroy objects for some reason, or if you're interfacing with C code and thus working with objects that don't have destructors

But in this case I need to clearly do something else than I'm doing, because of that circular shared ptr thing. I just need to figure out what would work better.

cheetah7071
Oct 20, 2010

honk honk
College Slice
If you're writing a complicated destructor, that's still usually a sign that something has gone wrong

In case it helps, here's the use case for weak_ptr:

weak_ptr points to an object that used to exist, and may or may not still exist. A weak_ptr existing won't prevent an object from destructing the way a unique_ptr or shared_ptr will. In order to use a weak_ptr, you ask it if the object it points to still exists--if it doesn't, you do some fallback case. If it does, you get a shared_ptr which you can use temporarily, to lock in the object continuing to exist for a little while. When you're done with it, you just let the shared_ptr go out of scope and the weak_ptr will stick around, pointing to an object which may or may not exist

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!

Ihmemies posted:

But in this case I need to clearly do something else than I'm doing, because of that circular shared ptr thing. I just need to figure out what would work better.
Given that loving Params crap I'm confident you could just use shared_ptr and have a memory leak (if the chapter struct even has a parent member, I didn't check) and whoever is teaching this poo poo won't notice or care.

If you were trying to actually write good code, the *general* rule is use unique_ptr unless you have a good reason to use shared_ptr and you know why. In this case you have a vector of things that clearly owns them; you can use bare pointers or references to navigate between them, then they get destroyed when that vector goes away, which is exactly what you want to happen.

Ihmemies
Oct 6, 2012

Course said don't mix regular and smart pointers. So I won't be mixing them. Since I don't understand at all the differences between smart pointers, and I understand even less about how to manually release memory of regular pointers when they are not needed anymore, I'll just keep using the shared pointers. The user can buy more RAM if he runs out of it because of shared pointer linking. I don't give a drat.

Xarn
Jun 26, 2015

roomforthetuna posted:

If you were trying to actually write good code, the *general* rule is use unique_ptr unless you have a good reason to use shared_ptr and you know why. In this case you have a vector of things that clearly owns them; you can use bare pointers or references to navigate between them, then they get destroyed when that vector goes away, which is exactly what you want to happen.

This is the correct approach

Ihmemies posted:

Course said don't mix regular and smart pointers. So I won't be mixing them. Since I don't understand at all the differences between smart pointers, and I understand even less about how to manually release memory of regular pointers when they are not needed anymore, I'll just keep using the shared pointers. The user can buy more RAM if he runs out of it because of shared pointer linking. I don't give a drat.

You should not mix regular and smart pointers for ownership. Mixing them in your code is pretty standard, as you use plain pointers for things that do not participate in ownership, but want to have potentially nullable look at something. references for things that do not participate in ownership (or will make a copy), but do not accept nulls, and so on.

Ihmemies
Oct 6, 2012

Sorry. This will use shared pointers for everything. I don't have the time or capacity to learn to do it any other way at this stage.

nielsm
Jun 1, 2009



For a short running program, leaking memory is fine anyway. Memory leaks matter in long running programs like server processes and GUI programs, but if you just load some data, process a bit, then exit, then cleaning up your own memory is likely to be a waste of effort.

See also the anecdote about the control program for a ballistic missile that was found to have a memory leak. No reason to fix it when the missile would explode before the memory ran out.

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!

Ihmemies posted:

Course said don't mix regular and smart pointers. So I won't be mixing them.
Yeah, that's what I'd expect from a teacher who recommends shared_ptr and presents you with that Params garbage like C++ is just a substitute for Python.

Ihmemies
Oct 6, 2012

Ideally I'd learn more about different types of smart pointers if I had the possibility to do a project from the ground up.

Now I don't understand enough about my program to refactor it again to work with different kind of pointers. I mean I got it to work "perfectly" as in the code gives expected printouts and it passes the tests.

Now the data sits in memory until program is closed, because of the linked shared pointers.

Anyways, uhh, maybe I could at least find a way to lessen the repetition. It has so many methods which have nearly the same contents, just copy pasted to other methods. Like printLongestInHierarchy vs printShortestInHierarchy, printParentsN, printSubchaptersN etc.

I have to figure some way to remove repetitive code, since it's penalized in grading...

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

Ihmemies posted:

Ideally I'd learn more about different types of smart pointers if I had the possibility to do a project from the ground up.

Now I don't understand enough about my program to refactor it again to work with different kind of pointers. I mean I got it to work "perfectly" as in the code gives expected printouts and it passes the tests.

Now the data sits in memory until program is closed, because of the linked shared pointers.

Anyways, uhh, maybe I could at least find a way to lessen the repetition. It has so many methods which have nearly the same contents, just copy pasted to other methods. Like printLongestInHierarchy vs printShortestInHierarchy, printParentsN, printSubchaptersN etc.

I have to figure some way to remove repetitive code, since it's penalized in grading...

Smart pointers are just objects with pointer semantics. The common uses of them are for garbage collecting (e.g. refcounted pointers) and managing ownership.

They're also useful when you have unusual memory layouts. E.g. a chunk of memory available through some bank switching mechanism. These get exposed via a series of methods. Often you'll track the bank, offset in that bank, and possibly length held. Smart pointers allow you to use this memory with a uniform syntax. Otherwise, to use your existing methods that take a reference to something you would need to pull that thing into a temp, call the method on the address of your temp, then write back if it was changed.

Supporting uniform memory access for banked memory via smart pointers is the feature that's made me most strongly consider moving to C++ from C. Haven't managed to convince myself it's worth it yet.

OddObserver
Apr 3, 2009
As much as I despise C, I would encourage you to first move to a platform that doesn't require banked memory. (Though I suppose I may need to congratulate you on working on something that's high volume enough to justify such a platform?)

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

OddObserver posted:

As much as I despise C, I would encourage you to first move to a platform that doesn't require banked memory. (Though I suppose I may need to congratulate you on working on something that's high volume enough to justify such a platform?)

Uhh.. it's a game for the loving wonderswan. I can't exactly get more main memory, but it has loads of SRAM available on the cart. And if I'm deranged enough to have started this project, recommending reasonable projects on reasonable platforms isn't going to fix my problem.

If I commercialize, I'd need to design and manufacture custom carts. I have plans for this, but haven't pulled the trigger on purchasing any of the stuff I'd need for it. If I follow through on manufacturing, i don't expect I'd get more than 100 sales (probably more like.. 5-10) for the WS/WSC cart. Though I have ports running on modern Windows, modern Mac, and Linux (as well as SVGA DOS, CGA dos, Gameboy Advance, Sega Saturn, and a few other platforms I'm forgetting about presently).

You can congratulate me on my career shift into management, which has freed up time and energy for dumb projects in my free time. :shrug:

OddObserver
Apr 3, 2009
Oh, I was guessing it was some sort of embedded thing where you had to save 50 cents on a chip so ended up with weirdly retro capabilities, rather than were doing the fun retro stuff....

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

OddObserver posted:

Oh, I was guessing it was some sort of embedded thing where you had to save 50 cents on a chip so ended up with weirdly retro capabilities, rather than were doing the fun retro stuff....

Don't get me wrong, I'd love doing the sad retro stuff too, but day job is service games which doesn't make me a strong candidate for cost optimization in the teledildonics industry or w/e still employs embedded people.

cheetah7071
Oct 20, 2010

honk honk
College Slice
I've been migrating my project to using conan and it went 90% well but Boost of all things is loving up. I figure Boost is big enough that someone here probably knows the intricacies of why things are going wrong.

Here's my entire conanfile.txt:

quote:

[requires]
zlib/1.2.13
boost/1.80.0
gdal/3.5.2
proj/9.0.1
libgeotiff/1.7.1
libtiff/4.4.0
xtl/0.7.4
gtest/cci.20210126
glfw/3.3.8
opengl/system

[generators]
cmake_find_package

And here's the part of my cmakelists.txt that's relevant to boost:

quote:

find_package(Boost REQUIRED)
include_directories(${Boost_program_options_INCLUDE_DIRS})
target_link_libraries(MyExe PRIVATE Boost::program_options)
target_link_libraries(MyTests PRIVATE Boost::program_options)

both conan and cmake run fine without warnings or errors. Checking the linker settings in Visual Studio, the only boost thing in my list of lib files is C:\.conan\9cebf8\1\lib\libboost_program_options.lib, which is a file that exists. When I try to build, however, I get this linker error: "LNK1104 cannot open file 'boost_program_options-vc143-mt-x64-1_80.lib'"

Normally I'd just assume that something in my cmake was generating incorrect settings, but like...I'm not even trying to link to that file. I recognize that filename, it's produced by building boost with some settings (and other settings give the filename that actually occurs in my linker settings). I'm pulling my hair out here trying to figure out why I'm getting a linker error for a file that I'm not even linking to. The same thing happens in my tests

I have no idea if this is a visual studio bug, an issue with my cmake file or somehow using conan wrong

Ihmemies
Oct 6, 2012

This week's C++ task said posted:

Many of the functions do similar things. You should think carefully, which utility functions to implement in order to avoid repeating code.


So. We have stuff like:
  • void close(Params& params), void open(Params& params)
  • void printParentsN(Params& params), void printSubchaptersN(Params& params)
  • void printLongestInHierarchy(Params& params), void printShortestInHierarchy(Params& params)

And I have been at the task, combining 2 methods to 1, avoiding repeating code, with ternary operators!

C++ code:
void Book::toggle_chapter(Params& params, bool isopen) const {
    string id = params.front();
    shared_ptr<Chapter> chapter = findChapter(id);

    // no chapter?
    if (chapter == nullptr) { return; }

    // open or close
    chapter->isopen_ = isopen;

    if (chapter->subchapters_.size() > 0) {
        for (auto& val : chapter->subchapters_) {
            // open
            if (isopen) {
                if (val->subchapters_.size() == 0 )
                    val->isopen_ = isopen;
            }
            // close subchapters
            else {
                toggle_chapter(vector<string> {val->id_}, isopen);
            }
        }
    }
}
C++ code:
void Book::print_pors_N(Params& params, bool isparents) const {
    string id = params.at(0);
    shared_ptr<Chapter> chapter = findChapter(id);

    // no chapter?
    if (chapter == nullptr) { return; }

    int level = stoi(params.at(1));

    // level must be at least 1.
    if (level < 1) {
        cout << "Error. Level can't be less than 1." << endl;
        return;
    }

    // no parent or subchapters?
    if (isparents
        ? chapter->parentChapter_ == nullptr
        : chapter->subchapters_.size() == 0
        ) {
        cout << id << " has no " << (isparents ? "parent " : "sub")
             << "chapters." << endl;
        return;
    }

    // collect a list of chapters so we can sort them before printing
    auto list = make_shared<vector<string>>();

    // populate list with parents or subchapters depending on what's wanted
    isparents
        ? get_parents(list, chapter, 0, level)
        : get_subchapters(list, id, 0, level);

    // sorts list to ASCII order with helper method compare
    std::sort (list->begin(), list->end(), compare);

    // Append to str and print result
    string result = id + " has " + to_string(list->size())
                  + (isparents ? " parent chapters:" : " subchapters:" );
    cout << result << endl;

    for (auto& val : *list) {
        cout << val << endl;
    }
}
C++ code:
void Book::print_sol_in_hierarchy(Params& params, bool islongest) const {
    string id = params.at(0);
    shared_ptr<Chapter> chapter = findChapter(id);

    // no chapter?
    if (chapter == nullptr) { return; }

    // get subchapters to depth n
    // TODO get_subchapters to work with inf depth
    auto list = make_shared<vector<string>>();
    get_subchapters(list, id, 0, 9999);

    string sol_id = id;
    int sol_len = chapter->length_;

    for (auto& val : *list) {
        if (islongest
            ? (idstore.at(val)->length_ > sol_len)
            : (idstore.at(val)->length_ < sol_len)
        ){
            sol_len = idstore.at(val)->length_;
            sol_id = val;
        }
    }

    // Append to str and print result. print() will be a c++23 feature
    string result = "With the length of " + to_string(sol_len) + ", "
                  + sol_id + " is the " + (islongest ? "longest" : "shortest")
                  + " chapter in " + (sol_id == id ? "their" : id + "'s")
                  + " hierarchy.";
    cout << result << endl;
}
Maybe I should stop, but I can't stop.

I have this like 7 times in different methods, still researching a way to combine them too. Maybe all the methods should call an utility method, which does this, and then that utility method calls the correct method? :v:

C++ code:
string id = params.front();
    shared_ptr<Chapter> chapter = findChapter(id);

    // no chapter?
    if (chapter == nullptr) { return; }
Although.. they don't strictly forbid us from editing the rest of the software:

E: nvm the comments in files forbid us from editing them: Student's don't touch this file.

This week's C++ task said posted:

You need not modify the ready-made functions. It is not necessary to understand the functionality of the modules Utils or Cli. However, since the topic of the round is modularity, this is a good example to be explored as a whole.

Ihmemies fucked around with this message at 20:56 on Nov 18, 2022

mobby_6kl
Aug 9, 2009

by Fluffdaddy

mobby_6kl posted:

I just created a new programming project for myself by being an idiot. I basically bricked the touchscreen on my laptop by trying to update it, because the response to the pen wasn't good and that was supposed to help. Unfortunately a) it turned out to be the wrong update, and it didn't check for compatibility, b) it actually flashed some settings into the controller so no amount of driver reinstalls will help.

I opened a support ticket but I'm not too optimistic and assume there's a good chance I'll have to solve it myself.

The good news is that it's probably fixable. The update was done with a basic console app in Windows, and the touch controller is I2C and I even found a datasheet for a slightly different model already. So if it could write the wrong configuration, it should also be possible to read it back from a known good device (I'd have to find a volunteer for that). Pretty simple, right?



https://www.crystalfontz.com/controllers/GOODIX/GT911ProgrammingGuide/478/

The problem is I've no idea where to even start on PC. I've actually done some I2C programming on an ESP32 but it had a pretty easy interface available and that's pretty much the entire code (other than some #defines) to initialize and read from a register:
C++ code:
static esp_err_t i2c_master_init(void)
{
    int i2c_master_port = I2C_MASTER_NUM;

    i2c_config_t conf = {
        .mode = I2C_MODE_MASTER,
        .sda_io_num = I2C_MASTER_SDA_IO,
        .scl_io_num = I2C_MASTER_SCL_IO,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = I2C_MASTER_FREQ_HZ,
    };

    i2c_param_config(i2c_master_port, &conf);

    return i2c_driver_install(i2c_master_port, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
}

static esp_err_t mpu9250_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
    return i2c_master_write_read_device(I2C_MASTER_NUM, MPU9250_SENSOR_ADDR, &reg_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}
Would anyone know what would be the equivalent approach to do this in Windows? Or Linux, in the worst case? While I could figure Linux out, I'd have to rely on strangers to run this for me and ideally step 1 wouldn't be "install gentoo".

E: I guess I need "HID-over-I2C": https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/hid-over-i2c-guide and maybe this user-mode application example: https://learn.microsoft.com/en-us/samples/microsoft/windows-driver-samples/hclient-sample-application/

E2: Going to park this for now, I found exactly one copy of the debug software for these controllers on some sketchy russian site that should be able to read/write the configs for me. That probably has a better chance of success :D
Welp I think this is back on the menu. The debug software does work, but requires installation of an unsigned driver (after putting windows into test mode) and there's no way I'll be able to talk a stranger on the internet into doing this for me. Because it sounds exactly like a social engineering scam.

The sample app doesn't seem to the the right thing, it's from loving 1998 and not related to HID over I2C, I guess the "Recommended content" list as automatically generated by AI. The actual page has no information about how to actually write anything for the driver.


Another option I thought is to reverse-engineer the app I have for updating the firmware. I found a thing called "Cutter" which seems to have a decompiler which is great because I'm poo poo at interpreting anything but basic asm.

leper khan posted:

Yes. Return to C

It's honestly a very pleasant language to work with. And it even gets useful features like embed before C++

You can always just implement any features you miss from C++ yourself :colbert:

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

mobby_6kl posted:

You can always just implement any features you miss from C++ yourself :colbert:

Per my previous post, everything but operator overloads, yes.

cheetah7071
Oct 20, 2010

honk honk
College Slice

while I am still deeply curious how I managed to get a situation where the linker was complaining about being unable to find a file I wasn't trying to link to, I switched to vcpkg and everything works now so good riddance, I guess

Beef
Jul 26, 2004

leper khan posted:

Per my previous post, everything but operator overloads, yes.

You have __Generic though.

qsvui
Aug 23, 2003
some crazy thing
I never really saw the point with _Generic. Don't you have to write a bunch of functions that handle each type? If a C programmer wants generics, they would just write one macro function that handles everything.

Adbot
ADBOT LOVES YOU

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
The point is that you get to specialize the implementation for specific types, instead of having to write convoluted code that will inherently compile and work correctly for every type you could possibly pass in.

Having a library of utility macro functions that work correctly regardless of the type of the expression you pass in is extremely useful for the times when you do want to write "one macro function that handles everything"!

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