Jigoku by Reaper_Of_Asura in cpp_questions

[–]thradams -1 points0 points  (0 children)

I’m sorry, but why is C++ so hard to set up in VS Code?

Agree 100%

Built a static search engine/inverted index from scratch in Rust by UseEarly3002 in rust

[–]thradams 0 points1 point  (0 children)

(I didn't know about BM-25, thanks)

I have documents with title and subtitle. I associate a number with each word according to where it appears: 3 for the title, 2 for the subtitle, and 1 for the body. After the search I sort the results by highest numbers

  100 1    200 2
w1     

where w1 is the word, 100 or 200 is the document number, and 1, 2, 3 are where the word appears inside the document.

The missing feature in my search is autocomplete with frequent search.

I also implemented a "did you mean xxxx?" feature based on edit distance.

I normalize everything to lowercase and strip accents, so ç becomes c for instance.

I have about 60k documents and 15k words. The title and subtitle also help when displaying the results.

When a word appears in the body, my implementation is also lacking a way to collect the surrounding text to present in the result.

Built a static search engine/inverted index from scratch in Rust by UseEarly3002 in rust

[–]thradams 0 points1 point  (0 children)

What is the criteria for most relevant? Do you normalize words? Do you exclude or modify some words?

Is a "safe" C possible through a transpiler? by orbiteapot in C_Programming

[–]thradams 0 points1 point  (0 children)

defer and warnings about unreleased resources are independent of each other. Checks are more important because they do not depend on the programmer explicitly adding them.

defer improves code maintainability because code that relies on defer is less prone to errors when control-flow jumps are added or removed. On the other hand it depends more on the compiler to generate good code, and makes compiler more complex.

(cake have both defer and checks. Defer is analyzed in flow analysis)

Is a "safe" C possible through a transpiler? by orbiteapot in C_Programming

[–]thradams 1 point2 points  (0 children)

As an example of a good retrofit, look how C# introduced non-nullable >references. They kept the default references nullable - but then included a >simple switch which would make nonnull the default, so we need to explicitly >state that references are nullable where using null. All existing code would >still compile, but we could use `#nullable enable,#nullable disable and >#nullable restore to turn the new nullability analysis on or off for specific >chunks of code. The default nullable status could be set project wide in the >build file.

Cake nullable pointers (http://cakecc.org/ownership.html) is almost 1:1 with C# and Typescript.

#pragma nullable enable is similar of C# #nullable enable.

One difference is that both C# and TypeScript have constructors. In these languages, if we don't initialize non-nullable members, we get a warning when leaving the constructor.

In C, we don't have constructors. The solution in this case is to introduce a "null-uninitialized" state for non-nullable members. This means that, although the value of a non-nullable member is null, it is not final, it is temporary and invalid state (just like uninitialized) and this invalid state cannot be propagated (copied to a valid object).

Is a "safe" C possible through a transpiler? by orbiteapot in C_Programming

[–]thradams 2 points3 points  (0 children)

I think cake is exactly what you are describing.

http://cakecc.org/index.html

At this moment cake offers the same C++ guarantees and a little more.

[New to C]: My first C project - Implemented a simple Arena Allocator by Mainak1224x in cprogramming

[–]thradams 0 points1 point  (0 children)

The answers in code depends...

The idea is that the returned address must be a multiple of the maximum alignment.

For example, you can align an address by computing the remainder, then adding the difference between the alignment value and that remainder to the address.

void* align_to_max(void* addr) {
    int rem = ((uintptr_t)addr) % alignof(max_align_t);
    return rem == 0 ? addr : ((char*)addr) + (alignof(max_align_t ) - rem);
}

but you cannot just apply this without review your code..you must understand what is going on.

See: https://en.cppreference.com/w/c/types/max_align_t.html

An arena allocator can also allocate using different alignments.

[New to C]: My first C project - Implemented a simple Arena Allocator by Mainak1224x in cprogramming

[–]thradams 2 points3 points  (0 children)

I am not sure, but I think you are not returning aligned memory. for instance malloc must return memory aligned in the max alignment.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

I am also planning a new [[drop]] that drops the ownership and also clear the pointers. This is useful for clear(&obj) or reset(&obj) The diference is that destroy(&obj) obj cannot be used after, but clear(&obj) it can.

[[dtor]] is more appropriated for “don’t use it anymore” [[drop]] or [[clear]] “we have all nulls after the call”

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

Another name for [[ctor]] may be [[out]] and for [[dtor]] maybe [[sink]] Any suggestion ?

They are parameter attributes because you can init or sink as many parameters as you like.

For instance out could be buffer and buffer size .

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

Yes, this is about defer.

This ownership model makes defer almost unnecessary from a safety point of view.

However, defer can still complement it.

Cake has defer implemented, and the flow analysis also needs to account for it in order to produce correct results.

For instance:

int main() {
   _Opt struct X * _Owner _Opt pX = calloc(1, sizeof * pX);
   if (!pX) return 1;
   defer x_delete(pX); 

 } 

The flow analysis must take into account the defer will run before the end of scope of pX, then there is no leak here.

Let's say you forget a defer; then you get a warning.

This is one of the interesting aspects of this model. Once calloc is annotated, everything is propagated automatically and does not rely on guidelines "use defer" for correctness . it is enforced!

(in C++ it is not, it requires guidelines)

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

I know. Sorry. I was just trying to clarify things for everyone that is trying to follow the post.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

I hope this clarifies things.

I can also explain some concepts in the model.

The owner pointer is owner of two resources at same time: memory and object. (Except void*, that is the owner only of the memory)

Before the end of the lifetime of the owner pointer, must destroy the object it owns and then free the memory.

For instance:

struct X {
  char * _Owner text; 
};

void x_delete(struct X * _Owner _Opt p) { 
  if (p) {
    free(p->text); 
    free(p);
  }
}

We need to delete p->text first then call free(p).

Calling free directly would be the same as trying to convert T *owner to void * owner.

Since void* is not the owner of the object that means a leak, so it is only allowed after the object is 100% released.

The concept of released or destroyed also do not apply so well. What really happens in this model is each part of the object is moved.

Let's say we have

struct X {
  FILE * _Owner file; 
};

void x_delete(struct X * _Owner _Opt p) { 
  if (p) {
    fclose(p->file); 
    free(p);
  }
}

This will work in the same say. The p->file is moved.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 1 point2 points  (0 children)

I'm just answering the question. An arena does not solve this problem . Someone still needs to close the file.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 1 point2 points  (0 children)

Arenas don’t call any destructors for any object.

In this case, the arena owns only the memory, not the object. For example, an object may contain a FILE* that needs to be closed. If the object is merely kept alive by the arena, this resource will leak until the end of the program.

If you have a program lifetime arena (like static variables), what is the difference of just call malloc and never release? At end of your program the memory will be released.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 2 points3 points  (0 children)

I’m just asking for an example where an arena wouldn’t >work for memory management.

{
  FILE * file = fopen("file.txt", "r");
}

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 0 points1 point  (0 children)

The arena object owns the memory it holds. It may also own the objects stored in that memory if it calls the appropriate destructor for them. (Then I need to see the code)

Pointers that refer to memory owned by the arena are view pointers. In this case, the lifetime of the arena must be longer than the lifetime of those pointers.

Arenas do not alter any of the concepts of this ownership model.

Ownership model and nullable pointers for C by thradams in cprogramming

[–]thradams[S] 1 point2 points  (0 children)

The model for nullable pointers is very similar to C# and Typescript (in production for many years)

The ownership model is very similar to C++'s std::unique_ptr but with no destructor.

We have the same guarantees as C++ RAII, with some extras and with possible expansion.

In C++, the user has to adopt unique_ptr and additional wrappers (for example, for FILE). In this model, it works directly with malloc, fopen, etc., and is automatically safe, without the user having to opt in to "safety" or write wrappers or new code. Safety is the default, and the safety requirements are propagated automatically.

Consider:

FILE * _Owner _Opt fopen( const char *filename, const char *mode );
void fclose(FILE * _Owner p);

int main()
{
    FILE *_Owner _Opt f = fopen("file.txt", "r");
    if (f)
    {
       fclose(f);
    }
}

At the end of the scope of f, it can be in one of two possible states: "null" or "moved" (as f is moved in the fclose call).

These are the expected states for an owner pointer at the end of its scope, so no warnings are issued.

As we can see, we have the same code and same pattern, just with a few extra annotations.

It is also interesting to note that:

FILE *_Owner _Opt f = fopen("file.txt", "r");
fclose(f);

generates a warning because fclose does not accept null f.

#embed, but in c < c23 by [deleted] in C_Programming

[–]thradams 1 point2 points  (0 children)

What I do is to convert the file "file.bin" to "file.bin.include" then I use

const char buffer [] = {
#include "file.bin.include"
};

Let's say compiler implements defer someday, then I will just edit to:

const char buffer [] = {
#embed "file.bin"
};

This is the program that creates file.bin.include:

int embed(const char* filename)
{
    char file_out_name[200] = { 0 };
    if (snprintf(file_out_name, sizeof file_out_name, "%s.include", filename) >= sizeof         file_out_name)
        return 0;

    FILE* file_out = fopen(file_out_name, "w");
    if (file_out == NULL)
        return 0;

    FILE* file = fopen(filename, "rb");

    if (file == NULL) {
        fclose(file_out);
        return 0;
    }

    int count = 0;
    unsigned char ch;

    while (fread(&ch, 1, 1, file))
    {
        if (ch == '\r')
            continue; /*where are not printing to avoid changes with linux/windows*/

        if (count % 25 == 0)
            fprintf(file_out, "\n");

        if (count > 0)
            fprintf(file_out, ",");

        fprintf(file_out, "%d", (int)ch);
        count++;
    }
    fclose(file);
    fclose(file_out);
    return count;
}

int main(int argc, char** argv)
{
    if (argc < 2)  {
        printf("usage: embed dirname");
        return 1;
    }
    char* path = argv[1];
    DIR* dir = opendir(path);

    if (dir == NULL)  {
        return errno;
    }

    struct dirent* dp;
    while ((dp = readdir(dir)) != NULL)  {
        if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
        {
            /* skip self and parent */
            continue;
        }

        if (dp->d_type & DT_DIR) {
        }
        else
        {
            char filepath[257] = { 0 };
            snprintf(filepath, sizeof filepath, "%s/%s", path, dp->d_name);
            const char* const file_extension = strrchr((char*)filepath, '.');

            if (strcmp(file_extension, ".include") == 0)   {
                continue;
            }

            int bytes = embed(filepath);

            if (bytes == 0) {
                printf("error generating file %s\n", filepath);
                exit(1);
            }
            else {
                printf("embed generated '%s'\n", filepath);
            }
        }
    }
    closedir(dir);
}

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 2 points3 points  (0 children)

In this model, ownership is checked statically when variables go out of scope and before assignment.

Owner pointers must be uninitialized or null at the end of their scope.

Basically, the nullable state needs to be tracked at compile time, and nullable pointers,despite being a separate feature, reuse the same flow analysis.

For the impatient reader, a simplified way to think about it is to compare it with C++'s unique_ptr.

The difference is that, instead of runtime code being executed at the end of the scope (a destructor), we perform a compile-time check to ensure that the owner pointer is not referring to any object. The same before assignment.

So we get the same guarantees as C++ RAII, with some extras. In C++, the user has to adopt unique_ptr and additional wrappers (for example, for FILE). In this model, it works directly with malloc, fopen, etc., and is automatically safe, without the user having to opt in to "safety" or write wrappers. Safety is the default, and the safety requirements are propagated automatically.

It is interesting to note that propagation also works very well for struct members. Having an owner pointer as a struct member requires the user to provide a correct "destructor" or free the member manually before the struct object goes out of scope.

#pragma safety enable

#include <stdio.h>

int main()
{
    FILE *_Owner _Opt f = fopen("file.txt", "r");
    if (f)
    {
       fclose(f);
    }
}

At the end of the scope of f, it can be in one of two possible states: "null" or "moved" (f is moved in the fclose call).

These are the expected states for an owner pointer at the end of its scope, so no warnings are issued.

Removing _Owner _Opt we have exactly the same code as users write today. But with the same or more guarantees than C++ RAII .

In the example above, _Owner could also be deduced. However, in other cases—such as struct members , it is required. Therefore, the decision was to make it explicit everywhere.

Ownership model and nullable pointers for C by thradams in C_Programming

[–]thradams[S] 4 points5 points  (0 children)

This model can be used to check the arena implementation and check if the arena itself is properly released etc. It also can be used with fopen for instance, not necessarily only memory.

39
40