#Organizing data about functions with mixed types for a macro

5 messages · Page 1 of 1 (latest)

ashen knot
#

I'm participating in Advent of Code to learn Rust, and trying to write a code runner for it. Unfortunately, my ambitions exceed my knowledge, and so I have a more broad question.

The framework needs to compose two functions, a generator and a solver, together. However, while the input of the generator is known, and the output of the solver is known, the intermediate type is not. This is because I want to implement multiple generators for a given problem, in order to compare their performance (example follows). I know what I want the output code to look like, and what the input code should look like, but not how to organize data in order to get there. Even some links to documentation would be helpful. I don't know what I'm supposed to be looking up, and the few tutorials on macros I've gone through so far have not exactly been as helpful as I would like.

#

Here's some example user code:

type DataLine = (u16, u16, u16, u16);
type DataLine2 = u64;

// Generators -------------------------------------------------------
#[aoc_generator(day4, parse)]
pub fn input_generator_tuple_parse(input: &str) -> Vec<DataLine> {
    todo!()
}

#[aoc_generator(day4, tuple_bits)]
pub fn input_generator_tuple_bitbang(input: &str) -> Vec<DataLine> {
    todo!()
}

#[aoc_generator(day4, uint_bits)]
pub fn input_generator_u64_bitbang(input: &str) -> Vec<DataLine2> {
    todo!()
}

// Solutions --------------------------------------------------------

#[aoc(day4, part1, tuplecmp)]
pub fn solve_part1_tuple_cmp(input: &[DataLine]) -> i32 {
    todo!()
}

#[aoc(day4, part1, uintcmp)]
pub fn solve_part1_uint_cmp(input: &[DataLine2]) -> i32 {
    todo!()
}

#[aoc(day4, part1, tuplebits)]
pub fn solve_part1_tuple_bitbang(input: &[DataLine]) -> i32 {
    todo!()
}

#[aoc(day4, part2, tuplecmp)]
pub fn solve_part2_tuple_cmp(input: &[DataLine]) -> i32 {
    todo!()
}

#[aoc(day4, part2, uintcmp)]
pub fn solve_part2_uint_cmp(input: &[DataLine2]) -> i32 {
    todo!()
}
#

I need to generate the following:

// Auto-generated section

fn p1_1(input: &str) -> i32 {
    solve_part1_tuple_cmp(&input_generator_tuple_parse(input))
}

fn p1_2(input: &str) -> i32 {
    solve_part1_tuple_bitbang(&input_generator_tuple_parse(input))
}

fn p1_3(input: &str) -> i32 {
    solve_part1_tuple_cmp(&input_generator_tuple_bitbang(input))
}

fn p1_4(input: &str) -> i32 {
    solve_part1_tuple_bitbang(&input_generator_tuple_bitbang(input))
}

fn p1_5(input: &str) -> i32 {
    solve_part1_uint_cmp(&input_generator_u64_bitbang(input))
}

fn p2_1(input: &str) -> i32 {
    solve_part2_tuple_cmp(&input_generator_tuple_bitbang(input))
}

fn p2_2(input: &str) -> i32 {
    solve_part2_uint_cmp(&input_generator_u64_bitbang(input))
}

const P1S: [for<'r> fn(&'r str) -> i32; 5] = [p1_1, p1_2, p1_3, p1_4, p1_5];

const P2S: [for<'r> fn(&'r str) -> i32; 2] = [p2_1, p2_2];
#

Once I an get P1S and P2S, it's easy enough to write up benchmarking code, write a main function, and so on. Here's a super-basic main that only technically does what I want it to:

fn main() {
    println!("## AOC 2022, Day 4 ----------");
    let solution_p1 = P1S[0](AOC_RAW_INPUT);
    println!("Part 1 / parse Solution: {}", solution_p1);
    let solution_p2 = P2S[0](AOC_RAW_INPUT);
    println!("Part 2 / parse Solution: {}", solution_p2);
    println!("Checking alternative solutions...");
    for (idx, solver) in P1S.iter().enumerate().skip(1) {
        let solution = solver(AOC_RAW_INPUT);
        if solution == solution_p1 {
            print!("✅");
        } else {
            println!("");
            println!("Solver dispute: #{} found {}", idx, solution);
        }
    }
    for (idx, solver) in P2S.iter().enumerate().skip(1) {
        let solution = solver(AOC_RAW_INPUT);
        if solution == solution_p2 {
            print!("✅");
        } else {
            println!("");
            println!("Solver dispute: #{} found {}", idx, solution);
        }
    }
    println!("");
    println!(" ---- Benches ----- ");
    run_benches();
}
#

So the quesion is how to write macros that generate the various p1_/p2_ functions and the two arrays P1S/P2S. Because I can't guarantee what the intermediate type is, traits become much harder. Because the generators don't all have the same type, I can't just put them all in a single list. And I can't just write up a hashmap to break up the type signatures, because the hash map itself needs a type signature.

So what do I do?