CS 330 Lecture 38 – Monkey patching, Meta-programming, Blocks, and Mixins

Agenda

  • what ?s
  • monkey patching
  • why monkey patch? a custom malloc
  • reflection and meta-programming
  • why meta-program? a testing framework
  • adding blocks to TestTest
  • mixins
  • making things Orderable (IRL, use Comparable)

?s

TODO

  • Write a question that could appear on the final exam. Derive your question from the topics posted under the previous lecture. Post in the comments for a participation point, using a name by which I can identity you. (First name and last initial is fine.) Post a second for extra credit. Do not turn in a piece of paper.

Code

monkey.rb

#!/usr/bin/env ruby

class Fixnum
  def [] i
    i + self
  end
end

puts 6[3]

mymalloc.cpp

#include <iostream>

const int NWORDS = 2048;
int heap[NWORDS];

char *malloc(int nwords) {
  int *block_start = (int *) heap;

  // Load up the metadata for the first block.
  int nwords_in_block = block_start[0];
  int is_free = block_start[1];

  // If the block is free and large enough, then we can give some portion
  // of it back to the caller. Otherwise, we check the next block to see
  // if it can satisfy the request.
  while (!is_free || nwords > nwords_in_block) {
    block_start += nwords_in_block + 2;
    nwords_in_block = block_start[0];
    is_free = block_start[1];
  }

  // Invariant: block_start points to a block that can hold the requested
  // number of bytes. (Or we're out of memory, a case I don't handle.)

  // This block holds has room for the requested number of words and is no
  // longer free.
  block_start[0] = nwords;
  block_start[1] = 0;

  // If the requested number of words is less than the actual capacity of
  // the block, then we split the block in two. The unused portion gets
  // marked free.
  if (nwords < nwords_in_block) {
    block_start[2 + nwords] = nwords_in_block - nwords - 2;
    block_start[2 + nwords + 1] = 1;
  }

  // The metadata exists in the first two words of this block. The actual
  // caller data starts two words later, so it's that address we give
  // back.
  return (char *) (block_start + 2);
};

void free(void *pointer) {
  // The caller will have sent us an address two words into the block that
  // was previously allocated with malloc. The block is marked free, but
  // it retains its size. A future malloc call can make use of it if it
  // provides enough words.
  int *block_start = ((int *) pointer) - 2;
  block_start[1] = 1;

  // A smarter implementation would coalesce adjacent free blocks to avoid
  // fragmentation.
}

void leak_check();

int main(int argc, char **argv) {
  // I've got to initialize the free list that is used to manage the heap.
  // I'm sure there's a better way to do this.
  ((int *) heap)[0] = NWORDS - 2;
  ((int *) heap)[1] = 1;

  int *a = (int *) malloc(4);
  free(a);
  a = (int *) malloc(8);
  free(a);

  // Are there any blocks still marked free?
  leak_check();

  return 0;
}






















void leak_check() {
  int *block_start = (int *) heap;

  while (block_start - heap < NWORDS) {
    int nwords_in_block = block_start[0];
    int is_free = block_start[1];

    if (!is_free) {
      std::cout << "a leak!" << std::endl;
    }

    block_start += nwords_in_block + 2;
  }
}

TestTest.rb

#!/usr/bin/env ruby

class TestTest
  # def TestTest.assert_equals(msg, expected, actual) 
    # if expected != actual 
      # puts "#{caller[0]} #{msg}" 
      # puts "Expected: #{expected}" 
      # puts "  Actual: #{actual}" 
    # end 
  # end 

  def TestTest.assert_equals(*args)
    msg = args[0]
    expected = args[1]
    actual = args.length == 3 ? args[2] : yield

    if expected != actual
      puts "#{caller[0]} #{msg}"
      puts "Expected: #{expected}"
      puts "  Actual: #{actual}"
    end
  end

  def TestTest.run(clazz)
    instance = clazz.new
    # puts instance.methods.sort.join(" ") 
    cases = instance.methods.grep(/^test/)
    for c in cases do
      # puts c.inspect 
      # instance.c 
      instance.send(c)
    end
  end
end

class UnitTests
  def testStringLength
    TestTest.assert_equals("String.length failed", 3, "abc".length)
  end

  def testStringSubscript
    TestTest.assert_equals("String[] failed", 'c', "abc"[2])
  end

  def testStringFailed
    TestTest.assert_equals("String[] failed", 'f', "abc"[2])
  end

  def testStringConcatenation
    TestTest.assert_equals("String concatenation failed", 'blarp') do
      s = 'b'
      s += 'l'
      s += 'ar'
      s += 'p'
    end
  end
end

TestTest.run UnitTests

# tests = UnitTests.new 
# tests.testStringLength 
# tests.testStringSubscript 
# tests.testStringFailed 

Haiku

class Haiku:
  def nsyllables x; [3, 12, 3][x]; end
Perversion

Comments

  1. Spencer G. says:

    What are the advantages and disadvantages of using a functional language like Haskell? Provide a real world example of when it would be beneficial to use.

  2. Aaron Emmert says:

    What is significant about anonymous functions, e.g. Haskell lambda functions?

  3. Aaron Emmert says:

    If you had to choose between ruby and c++ to implement some sort of inheritance, which language do you feel would be more appropriate for this task and why?

  4. Andrew Hazen says:

    You are working on a project with a large amount of code that needs to interact with similar methods. What would you rather use, mixins or inheritance? Provide an example of each and why the strengths might benefit such a project.

  5. Adam King says:

    The following code is rather patchwork to pass testing for sake of this question.

    Suppose a character has a “burning” instance variable that decides whether or not the character is currently on fire.


    def burninate(*args)
    other = args[0]

    damage = #Code goes here!

    other.takeHit(damage)
    end

    def ignite(other)
    if other.burning
    burninate(other, "i'm on fire!") #code goes here!
    else
    burninate other
    end
    end

    Given these functions:

    1. Change the burninate call in ignite to use a block statement to calculate extra damage (or healing?!) taken from already being on fire.

    2. In burninate, set damage accordingly (e.g. if one argument is sent in, set damage to 100; if more than one is sent in, use the damage calculated from the block in ignite. The “i’m on fire” parameter is just my patchwork way of signifying more than one arg is sent)

    Extra credit for absurdly complex and in-depth damage calculation in the ignite block, including but not limited to critical modifiers and elemental weaknesses!

  6. Sean Morrissey says:

    What are templates used for in c?

  7. Sam D says:

    What are some examples of a functional language, why is it used, and what are the advantages of this over imperative or object oriented programing? Write a list comprehension to enhance your explanation.

  8. john rankin says:

    question 1:
    what is a problem with
    multiple inheritanes that you need to becareful of? function name colisions…

    question 2:
    how do you solve this when there is this type of collision?

  9. David says:

    (True or false)

    The following statement would be valid in a functional programming paradigm:

    a = b
    a = c

    If not, explain why. If so, don’t explain why, because you are wrong. Start over.

    1. David says:

      This is not valid because you are giving the variable ‘a’ multiple definitions. In an imperative paradigm, this a would be given each definition sequentially, but in a functional paradigm, you’re just assigning multiple definitions, and the compiler/interpreter/machine has no good lead as to which definition should be used.

  10. David says:

    You have a collection of sheep. You want to separate the fluffy ones from the not-so-fluffy ones and move both groups into different sheep pens. To do this, you may choose between these three functions: map, filter, and reduce (you may not use “Ba ram ewe”). Which function would be most appropriate?

    Now that you have all your fluffy sheep in a pen, you want to sheer them and harvest the accumulated wool so you can sell it for bitcoins. Still choosing from the three previous functions, how would you accomplish this?

    1. David says:

      Use filter, then reduce. Then map more wool onto the sheep!

  11. Di L says:

    What’s your favorite programming language in 330? Explain why?

  12. David says:

    What is a programming language?

  13. David says:

    The king has just decreed that all functions shall no longer hold first class citizenship, henceforth and forevermore. However, he has not yet finalized his decision with the royal seal, and you may be able to change his mind, since you are his head adviser. What arguments will you use to persuade him that functions should be first class citizens?

    1. David says:

      Note: first class citizenship just means “treated like a variable”

  14. yakun says:

    How do you define a functional language?

  15. Joe Simon says:

    In what situations would you use shell scripting over writing a C/C++ program?

    1. David says:

      Is this final comprehensive, or does it just cover the last half of the class?

  16. Marc Allen says:

    Suppose you have the string “Chunky Bacon!” in Ruby. You come to the realization that, in its current form, “Chunky Bacon!” lacks the urgency you wish to express. You decide it best to instead produce the same string in all caps. Holding shift would be too much effort, so using caps lock seems like the obvious choice. However, your caps lock key is broken! How could one utilize the ruby language to take “Chunky Bacon!” and turn it into “CHUNKY BACON!”?

  17. David says:

    You are interviewing a potential employee for a software engineering position and you ask, “What are some advantages of object oriented programming?” The interviewee beams as he quickly reports, “Well, primarily code re-use, but definitely inheritance, too. These are primarily the two main features of object oriented programming.”

    Is this a good answer? Why or why not? What would be a better answer? (Hint: this is a bad answer. Shame on the interviewee. Shame!)

    1. David says:

      A better answer would be something along the lines of, “OOP is nice because it gives us encapsulation and polymorphism. Encapsulation lets us do more than just keep all our jank in one place. It lets us bind methods and operations to specific entities. It also lets us control access to stuff. It gives us the notion of “this.” Polymorphism lets us make generalizations when we’re talking about types, which is nice, because that’s how the real world is. A sort method won’t have to know about the underlying structure of a collection, as long as it knows that what it’s given is a collection in the way that it needs it to be.”

  18. Mitchell S says:

    What are the differences between statically and dynamically typed languages, and what are the advantages and disadvantages of each.

  19. Nolan K says:

    How does templatizing differ from subtype polymorphism? What reason would you have for using each?

    1. David says:

      https://en.wikipedia.org/wiki/Subtype_polymorphism
      https://en.wikipedia.org/wiki/Template_%28computer_science%29

      subtype polymorphism = inheritance or interface implementation
      templates / generics = composition

      Does anyone have anything to add to this?

  20. David says:

    Write a haiku in Ruby that expresses how you feel about the language. Don’t worry if the syntax has a couple errors. This is art.

  21. John F says:

    Write a line of code in Haskell that sums up all the numbers in an array using map

  22. Karlie D says:

    What languages compile at runtime? What are positives/negatives to this?

  23. Nick G says:

    Write a haskell function that takes in a list, and produces the fibonacci sequence up to 10000.

  24. Nick G says:

    If you had to chose one programming language that we’ve learned this semester, which would it be? Explain using topics covered such as polymorphism, abstraction, and preference in compile time vs run time execution of code.

    1. Nick G says:

      I should say, if you had to chose one to use over all the other languages for say an application (and state what application you’d be using it for)

  25. Nate Carr says:

    In what way are scripting languages such as shell scripting in Linux, and PowerShell scripting in windows similiar?

  26. Nate Carr says:

    What is the best way to approach debugging in a language which throws generic errors?

    1. David says:

      What is a good answer for this?

  27. JacobM says:

    Why are OOP languages not good for real time systems?

  28. JacobM says:

    What are the advantages and disadvantages of automatic garbage collection?

  29. Jake E says:

    In assembly, a common error is a seg fault without direction to what line it derived from. What might be a good way to figure out the occurrence of such a bug?

  30. Tyler T says:

    What is the difference between statically and dynamically typed programming languages? From the languages we covered this semester, name one statically-typed language and one dynamically-typed language.

Leave a Reply

Your email address will not be published. Required fields are marked *