Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FileIO library (read, write to file) #374

Merged
merged 7 commits into from
Apr 21, 2016
Merged

Conversation

PhucVH888
Copy link
Contributor

I have added the basic File IO library to Encore prototype along with the test.

The library has basic functions as the following:

  • opening and closing file.
  • reading line from a file.
  • writing a line (String, or char*) to a file.
  • end of file checking.

@EliasC
Copy link
Contributor

EliasC commented Apr 13, 2016

As far as I can see, I will never want to have a a File that is not (or has never been) open. Therefore I suggest merging fopen with the constructor so that typical usage becomes

let f = new File("foo.txt", "w");
f.write("foobar\n");
f.close();

while (f.isOpen()) {
print("{}",f.readline());
};
f.fclose();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit weird. "While f is open, read lines from f. When f is no longer open, close f". If f.isOpen() returns false, I would expect that I do not need to close it. Maybe f.eof() is a better name?

@EliasC
Copy link
Contributor

EliasC commented Apr 13, 2016

Minor comment, I think open and close are better names than fopen and fclose.

@supercooldave
Copy link

Just noting that you should create branches in your own copy of the repository and not in this copy (paraplu/encore) to keep the main repository as clean as possible.

Obviously, don't change anything for now. But for the next pull request.

-----------------------------------
-- Write to file.
-----------------------------------
def write(fout:String, fmode:String, content:String) : void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do I need to pass the out-file and mode to this method? Don't I already have this information in the corresponding fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@supercooldave : Oh, I am sorry for my bad. I used git push without specifying my repository's name.

@EliasC
Copy link
Contributor

EliasC commented Apr 13, 2016

At some point we could consider separating input streams from output streams to avoid having to guess if a File is for input or output, but I think this solution is good enough for now.

@supercooldave
Copy link

@pengstrom might look into doing some nicer IO based on Iteratees sometime in the future.

@PhucVH888
Copy link
Contributor Author

@EliasC : I agree with you, the names is quite misleading.

I think in general the name can be File but the identifiers such as fout = new File("test.txt","w") and fin = new File("test.txt","r") can be used to distinguish.

-----------------------------------
-- Write to file.
-----------------------------------
def write(fout:String, content:String) : void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fout argument is never used (it is passed to writeChar, which binds it to a variable that is never used). It should be removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(third time's the charm)
This argument is never used and should be removed.

@EliasC
Copy link
Contributor

EliasC commented Apr 13, 2016

I think in general the name can be File but the identifiers such as fout = new File("test.txt","w") and fin = new File("test.txt","r") can be used to distinguish.

I strongly disagree. We cannot force our users to use a good naming convention, but we can design our libraries in a way that helps the user. Since an instream and an outstream present different interfaces, they shouldn't be the same type. If I open a file with mode "w" calling readline should be disallowed. Similarly, if I open a file with mode "r" calling write should be disallowed.

With this said, I think that this could be fixed later.

@EliasC
Copy link
Contributor

EliasC commented Apr 13, 2016

(also, you will probably want to squash your commits for the final push)

@albertnetymk
Copy link
Contributor

The squash can be done on github via merge button. FYI.


def eof() : bool {
let f = this.file;
embed bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use embed bool feof(#{f}); end instead? (only asking since you need another commit to remove the fout argument from write and writeChar)

Copy link
Contributor Author

@PhucVH888 PhucVH888 Apr 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the embed bool feof(#{f}); end does not work correctly. It prints nothing.
While using previous implementation, it prints the file's content.

  def eof() : bool {
    let f = this.file;
    embed bool
      bool res = true;
      if (ungetc(fgetc(#{f}),#{f})==EOF) res = false;
      else res = true;
      res;
    end;
  }

@EliasC
Copy link
Contributor

EliasC commented Apr 19, 2016

Don't forget to fix the last tiny things so that we can get this merged :)

@PhucVH888
Copy link
Contributor Author

Thanks @EliasC . I have pushed the revised version.

@TobiasWrigstad
Copy link
Contributor

What about async I/O? I worry about adding a file abstraction that will kill performance … because we have a tendency of letting that file become the file, plus breaking legacy code.

@supercooldave
Copy link

@TobiasWrigstad: There is no legacy code.
Perhaps we create a subdirectory of the module hierarchy called "FutureDeprecated".

@EliasC
Copy link
Contributor

EliasC commented Apr 19, 2016

@TobiasWrigstad: I don't think that merging this is a big problem. There are other refactorings we should do here anyway, so it won't stay the file.

@kikofernandez
Copy link
Contributor

I think this is good enough! Better to have a synchronous version than nothing at all.
We can do the same as other programming languages, we can provide the synchronous and asynchronous implementation

@PhucVH888
Copy link
Contributor Author

@TobiasWrigstad : I think the asyn I/O would be better. The sync version is used when I review the RequestHttp PL, I think a FileIO module is necessary to write out the result. I also have another asyn I/O version. Should I push it here?

@TobiasWrigstad
Copy link
Contributor

TobiasWrigstad commented Apr 19, 2016

I think a FileIO module is necessary to write out the result

What does this mean?

I also have another asyn I/O version. Should I push it here?

Do you have an async file abstraction?

@PhucVH888
Copy link
Contributor Author

I think a FileIO module is necessary to write out the result
As I want to write the test result to a file, so I implemented sync IO version.

There is still segmentation fault with this async version.
IO.enc

class File {
   ....
}

IOTest.enc

class Main 
  def main () : void {
    let file = "IOTest.out";    
    let f    = new File(file,"w");

    let msg  = "Hello World!\n";

    -- Test write file
    f.open(file,"a");
    f.write(file,msg);
    f.close();

    -- Test read file
    f.open(file,"r");
    while (not (get f.eof())) {
      print("{}", get f.readline());
    };
    f.close();

    print "Finished!"
  }

@TobiasWrigstad
Copy link
Contributor

OK. I'm going with @kikofernandez's recommendation. Let's support what works now and add async I/O later.

def writeChar(fout:String, content:embed char* end) : void {
let
fout1 = fout.data
mode = this.mode.data
Copy link
Contributor

@EliasC EliasC Apr 19, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two variables also aren't used.

@EliasC
Copy link
Contributor

EliasC commented Apr 19, 2016

@PhucVH888: Please remove the unused variables from write and writeChar!

@PhucVH888
Copy link
Contributor Author

@EliasC : I have fixed it. Tack!

@EliasC
Copy link
Contributor

EliasC commented Apr 19, 2016

It seems the embed bool feof(#{f}); end does not work correctly. It prints nothing.

I think has uncovered a bug! Your implementation of eof returns true when the end of file has not been reached:

    while (f.eof()) {
      print("{}", f.readline());
    };

The conditional of this loop should be not f.eof(), i.e. "while we have not reached the end of the file". So I think you can go back to using feof (and update the test).

@PhucVH888
Copy link
Contributor Author

You are right. The very first name of the function is isOpen() and changing the name to eof() without adding not causes hidden bug. So, it's fair to use feof().

def readlineChar() : embed char* end {
let f = this.file;
embed (embed char* end)
char* line = encore_alloc(_ctx,255);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@albertnetymk: Does this code run a risk of an outdated context? Consider if readline is called in a loop that also does get.

(@PhucVH888: this is related to a bug discovered by Albert yesterday, you shouldn't worry about having introduced it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EliasC : Does it solve the outdated context if the _ctx is refreshed before passing to encore_alloc()?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, @albertnetymk's solution to these problems is calling encore_ctx() instead of using the (possibly stale) _ctx variable. I'm working on a PR to make sure that all embedded uses of encore_alloc refresh the context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think so.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then to be safe, let's change _ctx for a call to encore_ctx(), and then I think this is ready to be merged!

Copy link
Contributor Author

@PhucVH888 PhucVH888 Apr 20, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does encore_ctx() return _ctx?

Should I change like this char* line = encore_alloc(encore_ctx(),255);
or this encore_ctx(); char* line = encore_alloc(_ctx,255);?

Because it works in both ways.

@kikofernandez
Copy link
Contributor

kikofernandez commented Apr 20, 2016

I would do the following (since the evaluation of function arguments in C is not defined):

_ctx = encore_ctx();
char* line = encore_alloc(_ctx,255);

@albertnetymk
Copy link
Contributor

I don't think the evaluation order matters here. Actually, the one-liner looks more compact. Subjective, sure.

@kikofernandez
Copy link
Contributor

I read somewhere in stackoverflow that in C99, encore_alloc(encore_ctx(), 255) doesn't even guarantee that encore_ctx() gets evaluated before encore_alloc since this behavior is undefined.
I would rather be safe than sorry :)

@supercooldave
Copy link

@kikofernandez That's very surprising. How can a function call be evaluated before it's arguments ... except in Haskell.

@albertnetymk
Copy link
Contributor

I am only aware that arguments evaluation order is undefined in C, but that the evaluation could happen after the function call is beyond me. Reference?

@EliasC
Copy link
Contributor

EliasC commented Apr 20, 2016

@kikofernandez: I think only the order of evaluation is unspecified, so if you have foo(bar(), baz()) you cannot know which argument is evaluated first. You are however guaranteed that they are evaluated.

EDIT: What @albertnetymk said...

@kikofernandez
Copy link
Contributor

Ok, maybe this is not the most reliable source but I understand something different from here: http://www.geeksforgeeks.org/g-fact-20/

@EliasC
Copy link
Contributor

EliasC commented Apr 20, 2016

foo(x++, x++) is a different story! It has something to do with sequence points (and there not being one between the two instances of x++), but this doesn't apply to function calls.

@EliasC
Copy link
Contributor

EliasC commented Apr 20, 2016

Here is a good text on the subject: http://stackoverflow.com/a/4176333

@albertnetymk
Copy link
Contributor

For record purpose, I think inlining encore_ctx() in the function call is fine.

@PhucVH888
Copy link
Contributor Author

Let's try with inlining encore_ctx() in the function call.

@kikofernandez
Copy link
Contributor

kikofernandez commented Apr 21, 2016

I think this is ready to be merged! Merging in 30 min unless there are objections (I will try to auto-squash to see how it works :) )

@kikofernandez kikofernandez merged commit 0e6fac7 into development Apr 21, 2016
@kikofernandez kikofernandez deleted the file-io branch April 21, 2016 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants